函数可重入性(reentrant)

函数可重入性( 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/free 会访问全局内存管理链表,并且操作是非原子的,因而是不可重入的。

譬如 malloc 正在修改链表时,程序捕获到一个信号,信号处理函数中再次调用 malloc ,这很可能引发严重后果。

可通过全局锁实现线程安全版本的 malloc/free ,但它可能引发性能问题。

问题:信号处理的控制流是怎样的?全局锁会造成 malloc 死锁吗?信号处理期间所有线程应该都会被暂停。某个子线程正在 malloc 时被中断,信号处理函数中的 malloc 会死锁吗?

函数可重入的条件

不能含有静态(全局)非常量数据。

不能返回静态(全局)非常量数据的地址。

只能处理由调用者提供的数据。

不能依赖于 单实例模式 资源的锁。

不能调用不可重入的函数,如 malloc/free

 

小心进程范围内的全局变量,如 errno h_errno

下面的代码存在 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

 

 

你可能感兴趣的:(多线程,String,2010)