首页 > 代码库 > 系统调用与信号重启,好
系统调用与信号重启,好
这篇写的很好
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的实现方式。
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; }
原文中对比了三种方案。
第一种,是不判断EINTR.
第二种,是判断EINTR,然后重启。
第三种,是对于信号处理函数,sigaction,里面sa_flags 加上对 SA_RESTART的支持,这样被中断的系统调用就能够自动重启了。
注意,和sleep是不一样的。sleep是返回的秒数可能不足。
http://www.cnblogs.com/charlesblc/p/6434213.html
返回剩余的秒数,而不是errno=EINTR。
sleep的例子应该是,SIGALARM,因为本身sleep就是用SIGALARM实现的。
系统调用与信号重启,好