函数可重入性( reentrant )
Version 1.0
2010-4-25
多线程或异常控制流情况下,函数执行过程中控制流被打断而去执行另一个函数,“另一个函数”可能刚好是被打断的函数本身,从而产生“重入”问题。
例如,程序在执行函数 foo() 时捕获了信号,而信号处理函数又调用了 foo() ,于是发生“重入”。
“若一个 程序 或 子程序 可以安全的被 并行 执行,则称其为可重入。即当该子程序正在运行时,可以再次进入并执行它。”
该定义似乎不具备良好可操作性。
按定义,递归不是重入的场景。
函数是可重入( reentrant )的,是指对于相同的合法函数参数(包括无参函数的情况),多次调用此函数产生的行为是可预期的,即函数的行为一致,或者结果相同。不能保证这一点的函数是不可重入函数。
理解:函数输出是可预期的,与函数的调用次序、次数、场景(多线程调用、信号处理函数中调用)等无关,则函数可重入。
可重入是线程安全的充分条件。但线程安全并不表明函数可重入,如下面的函数是线程安全的,但不可重入:
String g_str;
String getStr()
{
String a;
mutex.lock;
g_str+=“123”;
a=g_str;
mutex.unlock;
return a;
}
如果在信号处理函数中调用上述函数,则函数输出不是可预测的。类似函数还有很多,如使用了加锁保护的静态变量的函数。
线程安全只是保证了函数被多线程调用时数据不被破坏,不产生脏数据。线程安全要解决的问题是多线程访问资源时的冲突处理。一般通过简单加锁能实现线程安全,但不一定就可实现可重入。重入性影响函数外部接口,而线程安全只关心函数内部实现。
通常加锁方式针对不同线程的访问,但重入问题可能发生在同一个线程。
线程安全并不等于处理逻辑的正确,不等于函数行为、或输出的可预测性。
Malloc/free 会访问全局内存管理链表,并且操作是非原子的,因而是不可重入的。
譬如 malloc 正在修改链表时,程序捕获到一个信号,信号处理函数中再次调用 malloc ,这很可能引发严重后果。
可通过全局锁实现线程安全版本的 malloc/free ,但它可能引发性能问题。
问题:信号处理的控制流是怎样的?全局锁会造成 malloc 死锁吗?信号处理期间所有线程应该都会被暂停。某个子线程正在 malloc 时被中断,信号处理函数中的 malloc 会死锁吗?
不能含有静态(全局)非常量数据。
不能返回静态(全局)非常量数据的地址。
只能处理由调用者提供的数据。
不能依赖于 单实例模式 资源的锁。
不能调用不可重入的函数,如 malloc/free 。
下面的代码存在 bug ,原因是 errno 可能被信号处理函数改变。解决办法是在信号处理函数中保存 error 值,函数返回前恢复 error 。
if (close(fd) < 0) {
fprintf(stderr, "Error in close, errno: %d", errno);
exit(1);
}
[1] 可重入函数的概念, http://i-love-mzd.blog.sohu.com/76609080.html
[2] 可重入函数与线程安全函数, http://blog.mcuol.com/User/tope001/Article/14455_1.htm
[3] http://bbs.chinaunix.net/viewthread.php?tid=942090&extra=&page=1
[4] www.wikipedia.org
[5] 使用可重入函数进行更安全的信号处理 http://www.ibm.com/developerworks/cn/linux