Reentrant Functions可重入函数

 

10.6 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)某些函数调用了malloc或free。(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 <pwd.h>
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);
          }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值