Chinaunix首页 | 论坛 | 博客
  • 博客访问: 885115
  • 博文数量: 254
  • 博客积分: 5350
  • 博客等级: 大校
  • 技术积分: 2045
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 13:27
文章分类

全部博文(254)

文章存档

2015年(1)

2014年(9)

2013年(17)

2012年(30)

2011年(150)

2010年(17)

2009年(28)

2008年(2)

分类: C/C++

2011-10-20 15:47:24

Reentrant Functions可重入函数

(翻译by linxuleio)

   当一个进程捕捉到信号,进程执行的正常指令流程被signal handler(自定义的信号处理函数)临时打断。这时进程转而执行signal handler里的指令。当signal handler执行完返回,进程从之前被信号打断的代码处继续执行正常的指令流程。(进程对信号处理流程类似硬件中断处理。)但是在signal handler里,如果进程接收到信号,我们是无法知道进程执行的位置。

   当进程正调用malloc分配额外内存空间时接收到信号,同时我们在signal handler里也调用malloc函数时会发生什么?或则,当进程在调用某个函数(例如getpwnam函数会将返回结果存储在内存静态区域)时接收到信号,同时我们在signal handler里也调用同样的函数时会发生什么?以malloc为例子,因为malloc维护了一个所有已分配内存的链表。当进程接收到信号的时候,malloc可能正在修改它的链表,这时在signal handler又一次修改链表将对进程造成巨大灾难。在getpwnam的例子里,进程调用getpwnam函数返回的结果会把signal handler里调用getpwnam函数返回结果给覆盖掉。

   为了避免这些问题,因此UNIX系统定义了一些函数允许在signal handler里重复调用。下边的列表为可重入函数。

UNIX大多数函数不属于可重入函数。这是因为(a)某些函数内使用静态数据结构。(b)某些函数调用了mallocfree(c)属于标准I/O库的函数。大部分标准I/O库函数使用了全局数据结构,所以不属于可重入函数。

   注意尽管之前的一些例子,我们在signal handlers里调用了printf函数。因为会有可能在main中调用printf函数时产生信号中断,最后printf就不会输出我们期望的结果,

   signal handler调用可重入函数时,还有一件事要求我们注意。考虑到signal handler调用可重入函数时可能会改变进程原errno变量的值。比如在signal handler中调用read函数,read会改变errno的值,原先进程在main中执行程序所保存的errno的值会在signal handler中被改写。因此通用规则是,当我们在signal handler中调用可重入函数时,最好先保存下errno的值。(比如信号SIGCHLD,它的信号处理函数通常是调用wait函数。所有的wait函数都会更改errrno)

Example

10.5展示了一段在signal handler中每2秒调用一次非重入函数getpwnam的代码。

当这段程序运行的时候,输出结果是任意的。程序接受到SIGSEGV信号,在signal handler返回后应当终止。但实际上当signal handler里调用非重入函数getpwnam时程序里的一些内部指针被打乱。有时候,程序在接受SIGSEGV信号后,崩溃之前会运行一段时间。main函数在接受到信号后之后运行,有时这个返回值是正确的有时又是错误的。

这个例子告诉我们,一旦我们在signal handler里调用一个非重入函数,程序的结果是不可预知的。

Figure 10.5. Call a nonreentrant function from a signal handler

#include "apue.h" #include static void my_alarm(int signo) {     struct passwd   *rootptr;     printf("in signal handler\n");     if ((rootptr = getpwnam("root")) == NULL)             err_sys("getpwnam(root) error");     alarm(1); } int main(void) {     struct passwd   *ptr;     signal(SIGALRM, my_alarm);     alarm(1);     for ( ; ; ) {         if ((ptr = getpwnam("sar")) == NULL)             err_sys("getpwnam error");         if (strcmp(ptr->pw_name, "sar") != 0)             printf("return value corrupted!, pw_name = %s\n",                     ptr->pw_name);     } }
阅读(886) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~