首页 > 代码库 > 系统调用与信号重启,好

系统调用与信号重启,好

这篇写的很好

http://blog.chinaunix.net/uid-24774106-id-3065234.html

 

UNIX系统编程,这本书中有大量的重启系统调用,例如下面的例子:选自P50,

pid_t r_wait(int *stat_loc)
{
    int retval;
    while(((retval = wait(stat_loc)) ==-1 && (errno == EINTR));
    return retval;
}

还有对read,write的重启操作。

 

UNP volume1中提到slow system call,UNP中的例子是accept系统调用,accept是服务器端接受网络客户端的连接,connect,将完成三次握手协议的连接返回。如果服务器打开了监听listen,但是始终没有客户端来连接connect,可以想见,accept就阻塞在此。这是一种比较好理解的阻塞型系统调用。

wait也是一种阻塞性的调用。

 

但是linux世界上还有另外一种东东,叫做信号,来处理突发事件。

 

一般来讲,一个系统调用,要么成功,要么失败,但是由于为了及时处理信号,出现了第三种情况,系统调用被信号中断,为了标识这种情况,错误码errno置为EINTR。

 

我们可以看到这种方式并不优美,程序员需要自己判断errno,如果被信号终端,那么还需要自己来重启系统调用。这是System V UNIX的实现方式。

 

BSD 内核想程序员之所想,急程序员之所急,采用了另外一种实现,就是如果中断系统调用,切换到用户态来执行信号处理程序,那么系统调用没有返回值,内核在信号处理函数结束后,自动重启系统调用。这种方式很贴心阿,程序员再也不用自己判断errno,然后重启系统调用了。

 

LINUX不愧是UNIX世界的杰出新秀,他通过SA_RESTART 就可以支持BSD方案。

 

 

技术分享
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>
#include<string.h>

pid_t r_wait(int *stat_loc)
{
    int ret;
        while(((ret = wait(stat_loc)) == -1) )
        {
                /*if(errno == EINTR)
                {
                     fprintf(stderr,"may be interrupted by a signal,let wait again \n");
                }
                else*/
                {
                        break;
                }
        }

        return ret;
}

void sig_alrm_func()
{
    printf("catch an alarm signal\n");
    return;
}

int main(int argc,char** argv)
{
    pid_t childpid;
        int i,n;
        struct sigaction act;

        if(argc != 2)
        {
         fprintf(stderr,"usage : test n \n",argv[0]);
             return -1;
        }

        n = atoi(argv[1]);
        
        act.sa_handler = sig_alrm_func;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        // act.sa_flags |= SA_RESTART;
        sigaction(SIGALRM,&act,NULL);
        for(i = 0;i<n;i++)
        {
         if((childpid = fork()) <= 0)
                                break;
        }

       if(childpid == 0 )
        {
               sleep(50);
        }
        while(r_wait(NULL) >0) ;
        fprintf(stderr," i :%d process ID : %ld,\t parent ID :%ld \tchild ID : %ld\n",
                                        i,(long)getpid(),(long)getppid(),(long)childpid);

        return 0;
}
View Code

 

 

原文中对比了三种方案。

 

第一种,是不判断EINTR.

第二种,是判断EINTR,然后重启。

第三种,是对于信号处理函数,sigaction,里面sa_flags 加上对 SA_RESTART的支持,这样被中断的系统调用就能够自动重启了。

 

 

注意,和sleep是不一样的。sleep是返回的秒数可能不足。

http://www.cnblogs.com/charlesblc/p/6434213.html

返回剩余的秒数,而不是errno=EINTR。

sleep的例子应该是,SIGALARM,因为本身sleep就是用SIGALARM实现的。

 

系统调用与信号重启,好