unix重启系统调用解析
来源:互联网 发布:红蜘蛛端口号怎么修改 编辑:程序博客网 时间:2024/06/05 11:57
在unix高级环境编程一书列举了大量的重启系统,例如下面的例子:
pid_t r_wait(int *stat_loc) { int retval; while(((retval = wait(stat_loc)) ==-1 && (errno == EINTR)); return retval; }
还有对read,write的重启操作。刚开始对这些东西并不了解,不知道为啥需要判断errno,为啥errno为EINTR时,需要重新启动wait ,read,write。随着自己看的书变多,慢慢理解了重启系统调用。UNP volume1中提到slow system call,UNP中的例子是accept系统调用,accept是服务器端接受网络客户端的连接,connect,将完成三次握手协议的连接返回。如果服务器打开了监听listen,但是始终没有客户端来连接connect,可以想见,accept就阻塞在此。这是一种比较好理解的阻塞型系统调用。wait 也比较容易理解,父进程通过fork创建了子进程后,调用wait 等待子进程退出,但是子进程有自己的生命历程,也许子进程立即就退出了,也许子进程运行了5天还是没有退出,这都是可能的,所以wait也是一种阻塞性的调用。
但是linux世界上还有另外一种东东,叫做信号,来处理突发事件。如果系统调用尤其是阻塞型的系统调用遇到信号,怎么办呢?是等系统调用game over以后再处理信号,还是中断系统调用,尽快将信号投递到进程呢? 想想前面提到的例子,如果wait等的子进程5天后才能退出,父进程的信号投递将等的花儿都谢了。所以对于阻塞性的调用,必须阻止这种情况的发生。一般来讲,一个系统调用,要么成功,要么失败,但是由于为了及时处理信号,出现了第三种情况,系统调用被信号中断,为了标识这种情况,错误码errno置为EINTR。我们看到了,这个世界并不完美,编程同样也不完美。这也就是前文引用的errno == EINTR时,重启wait的原因。
我们可以看到这种方式并不优美,程序员需要自己判断errno,如果被信号终端,那么还需要自己来重启系统调用。这是System V UNITX的实现方式。
BSD 内核想程序员之所想,急程序员之所急,采用了另外一种实现,就是如果中断系统调用,切换到用户态来执行信号处理程序,那么系统调用没有返回值,内核在信号处理函数结束后,自动重启系统调用。这种方式很贴心阿,程序员再也不用自己判断errno,然后重启系统调用了。
LINUX不愧是UNIX世界的杰出新秀,他通过SA_RESTART 就可以支持BSD方案。下面我通过一个例子来讲述这个重启问题。
下面这个实例捕捉SIGALRM信号,捕捉到信号后,执行sig_alrm_func函数。我们可以看到因为errno ==EINTR这一段被注释掉,所以,没有重启系统调用wait,导致系统调用wait返回失败,并且父进程没有delay 50 second,从而,父进程先结束了,生成的子进程变成了孤儿。
#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;//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; }
运行结果:
root@libin:~/program/C/UNP/research# ./test 6 另外一个端口查看ps -ef ,并向父进程发送SIGALRM信号。 root 8679 7694 0 15:04 pts/0 00:00:00 ./test 6 root 8680 8679 0 15:04 pts/0 00:00:00 ./test 6 root 8681 8679 0 15:04 pts/0 00:00:00 ./test 6 root 8682 8679 0 15:04 pts/0 00:00:00 ./test 6 root 8683 8679 0 15:04 pts/0 00:00:00 ./test 6 root 8684 8679 0 15:04 pts/0 00:00:00 ./test 6 root 8685 8679 0 15:04 pts/0 00:00:00 ./test 6 root@libin:~# kill -SIGALRM 8679 发送信号SIGALRM后 执行端口显示 catch an alarm signal i :6 process ID : 8679,parent ID :7694 child ID : 8685 过了若干秒后,子进程完成自己的生命周期,退出。脱出之前,我们看到,子进程的父进程已经不再是8679.因为8679已经退出了,他们的新父亲是1号进程。 root@libin:~/program/C/UNP/research# i :0 process ID : 8680,parent ID :1 child ID : 0 i :1 process ID : 8681,parent ID :1 child ID : 0 i :2 process ID : 8682,parent ID :1 child ID : 0 i :3 process ID : 8683,parent ID :1 child ID : 0 i :4 process ID : 8684,parent ID :1 child ID : 0 i :5 process ID : 8685,parent ID :1 child ID : 0
通过前面的例子,我们看到了,如果我们收到信号,中断了系统调用,如果我们不重启系统调用,那么,程序不是按照我们预想的方式运行。
OK,我们将r_wait函数中的注释去掉,我们判断错误码errno,如果errno == EINTR,那么我们重启wait函数,那么,我们可以看到如下情景:
root@libin:~/program/C/UNP/research# ./test 6在另外一个端口执行ps -ef并且给父进程发送SIGALRM信号root 8733 7694 0 15:21 pts/0 00:00:00 ./test 6root 8734 8733 0 15:21 pts/0 00:00:00 ./test 6root 8735 8733 0 15:21 pts/0 00:00:00 ./test 6root 8736 8733 0 15:21 pts/0 00:00:00 ./test 6root 8737 8733 0 15:21 pts/0 00:00:00 ./test 6root 8738 8733 0 15:21 pts/0 00:00:00 ./test 6root 8739 8733 0 15:21 pts/0 00:00:00 ./test 6root@libin:~# kill -SIGALRM 8733在执行端口,我们看到,程序运行50s后,有如下打印: catch an alarm signal may be interrupted by a signal,let wait again i :1 process ID : 8735, parent ID :8733 child ID : 0 i :0 process ID : 8734, parent ID :8733 child ID : 0 i :2 process ID : 8736, parent ID :8733 child ID : 0 i :3 process ID : 8737, parent ID :8733 child ID : 0 i :4 process ID : 8738, parent ID :8733 child ID : 0 i :5 process ID : 8739, parent ID :8733 child ID : 0 i :6 process ID : 8733, parent ID :7694 child ID : 8739最后,我们试一下BSD方案,r_wait函数不判断errno,不重启wait,但是act.sa_flags = SA_RESTART.
root@libin:~/program/C/UNP/research# ./test 6 catch an alarm signal i :0 process ID : 8775, parent ID :8774 child ID : 0 i :1 process ID : 8776, parent ID :8774 child ID : 0 i :2 process ID : 8777, parent ID :8774 child ID : 0 i :3 process ID : 8778, parent ID :8774 child ID : 0 i :4 process ID : 8779, parent ID :8774 child ID : 0 i :5 process ID : 8780, parent ID :8774 child ID : 0 i :6 process ID : 8774, parent ID :7694 child ID : 8780看到了,情况和sa_flags = 0同时重启系统调用是一样的。
- unix重启系统调用解析
- unix系统调用(转载)
- unix系统之系统调用
- unix系统调用 open 函数
- Unix文件属性相关系统调用
- Unix 系统调用出错处理
- unix系统之系统调用的封装
- wait系统调用解析
- Linux系统调用解析
- wait系统调用解析
- Linux系统调用解析
- readdir系统调用解析
- 慢系统调用的中断与重启
- 信号处理之自动重启系统调用
- Android调用系统关机与重启功能
- [RK3399][Android6.0] 系统重启调用过程小结
- Android app调用系统重启、关机、获取root权限
- 解析Linux系统修改时区不用重启方法
- 接口与抽象类的理解日志
- VC++ 通过INI配置文件,实现多国语言切换界面
- 集线器与网桥的原理和作用
- 九度 OJ 1065:输出梯形
- spark快速大数据分析之数据读取与保存
- unix重启系统调用解析
- C++中getline()函数简介
- 类似网易新闻首页的轮播图,写了个自创的框架
- wanthelaping--1.1QEMU用户态框架
- poj1132 简单模拟
- 执行sh ./xxx.sh出现:“Syntax error: “(” unexpected”的解决方法
- iOS - 封装 - UIAlertController
- BZOJ 1355 [Baltic2009]Radio Transmission KMP
- 【例题】【动规(最大连续子序列)】NKOJ 1049 最佳游览