reentrant,thread-safe 和 async-signal-safe

先上定义吧,POSIX对它们的定义分别是:

Reentrant Function

A function whose effect, when called by two or more threads, is guaranteed to be as if the threads each executed the function one after another in an undefined order, even if the actual execution is interleaved.

Thread-Safe

A function that may be safely invoked concurrently by multiple threads. Each function defined in the System Interfaces volume of IEEE Std 1003.1-2001 is thread-safe unless explicitly stated otherwise.

Async-Signal-Safe Function

A function that may be invoked, without restriction, from signal-catching functions. No function is async-signal-safe unless explicitly described as such.

可重入我们都清楚,顾名思义,就是可以重新进入,进一步讲就是,用相同的输入,每次调用函数一定会返回相同的结果。这就是可重入。wikipedia上有更严谨的定义:

* Must hold no static (global) non-constant data.
* Must not return the address to static (global) non-constant data.
* Must work only on the data provided to it by the caller.
* Must not rely on locks to singleton resources.
* Must not call non-reentrant computer programs or routines.

然后是线程安全,从定义上看,它仅要求了可以安全地被线程并发执行。这是一个相对较低的要求,因为它内部可以访问全局变量或静态变量,不过需要加锁,也就是说,只要是在线程可控之中的,每次调用它返回不同的结果也没关系。到这里我们可以看出:可重入函数一定是线程安全的,而反之未必。wikipedia上也写道:

Therefore, reentrancy is a more fundamental property than thread-safety and by definition, leads to thread-safety: Every reentrant function is thread-safe, however, not every thread-safe function is reentrant.

例子,有很多,最出名的莫过于strtok(3),我们认识可重入这个概念就是从它开始的,它内部适用了静态变量,显然是不可重入的(它的可重入版是strtok_r(3))。其次应该是malloc(3),嘿嘿,其实也很明显,我就不多说了。但是,strtok(3)不是线程安全的,而malloc(3)是。

还有一个概念我们不常碰到,那就是异步信号安全,它其实也很简单,就是一个函数可以在信号处理函数中被安全地调用。看起来它似乎和线程安全类似,其实不然,我们知道信号是异步产生的,但是,信号处理函数是打断主函数(相对于信号处理函数)后执行,执行完后又返回主函数中去的。也就是说,它不是并发的!

一个函数,它访问了全局变量,那么它就是不可重入的,不过我们可以把它变成线程安全的,加上锁就可以,但是这种方法并不会把它变成异步信号安全的,而几乎可以肯定的是,使用了锁的一定不是信号安全的(除非屏蔽了信号,显然),信号完全可以在锁加上后解开前到来,然后就很可能形成死锁…… 这里有个很好的例子。所以,可重入的函数也一定是异步信号安全的,而反之未必。可以参考IBM上一篇不错的文章

关于异步信号安全的函数列表可以参考man 7 signal;关于线程安全的函数列表可以参考APUE第12.5节 ;关于可重入函数列表,可参考APUE第10.6节。另请参阅

阅读更多
换一批

keil报错(reentrant用法)

07-03

源程序:#include "httpd.h"rn#include "fs.h"rnrn#include rn#include rnrnstatic u8_t print_stats(u8_t next) reentrant;rnstatic u8_t file_stats(u8_t next);rnstatic u8_t tcp_stats(u8_t next) reentrant;rnrncgifunction code cgitab[] = rn print_stats, /* CGI function "a" */rn file_stats, /* CGI function "b" */rn tcp_stats, /* CGI function "c" */rn;rnrnstatic u8_trnprint_stats(u8_t next)reentrantrn...rnrnstatic u8_trnfile_stats(u8_t next)rn...rnrnstatic u8_trntcp_stats(u8_t next)rn...rnrnBuild target 'Target 1'rncompiling cgi.c...rnCGI.C(75): warning C182: pointer to different objectsrnCGI.C(77): warning C182: pointer to different objectsrnCGI.C(182): error C231: 'tcp_stats': redefinitionrnTarget not createdrnrn重点是:rnstatic u8_t print_stats(u8_t next) reentrant;rnstatic u8_t file_stats(u8_t next);rnstatic u8_t tcp_stats(u8_t next) reentrant;部分rn原来我没有添加reentrant是, keil的报警是:rn*** WARNING L13: RECURSIVE CALL TO SEGMENTrn SEGMENT: ?CO?CGIrn CALLER: ?PR?_PRINT_STATS?CGIrn*** WARNING L13: RECURSIVE CALL TO SEGMENTrn SEGMENT: ?CO?CGIrn CALLER: ?PR?_TCP_STATS?CGIrn*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESSrn SEGMENT: ?PR?_ETHERDEV_DELAY_MS?ETHERDEVrn*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESSrn SEGMENT: ?PR?_UIP_UNLISTEN?UIPrnrn然后我依据网上的建议添加了reentrant关键字,又有新的问题出现了。rn请哪位大侠告诉我应该怎么改!不胜感激(上述程序为uIP0.9中的cgi.c文件)

没有更多推荐了,返回首页