• 重启系统调用探究 ( 转)


     
         但是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,从而,父进程先结束了,生成的子进程变成了孤儿。
    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. #include<unistd.h>
    4. #include<sys/wait.h>
    5. #include<errno.h>
    6. #include<string.h>
    7. pid_t r_wait(int *stat_loc)
    8. {
    9.     int ret;
    10.         while(((ret = wait(stat_loc)) == -1) )
    11.         {
    12.                 /*if(errno == EINTR)
    13.                 {
    14.                      fprintf(stderr,"may be interrupted by a signal,let wait again ");
    15.                 }
    16.                 else*/
    17.                 {
    18.                         break;
    19.                 }
    20.         }
    21.         return ret;
    22. }
    23. void sig_alrm_func()
    24. {
    25.     printf("catch an alarm signal ");
    26.     return;
    27. }
    28. int main(int argc,char** argv)
    29. {
    30.     pid_t childpid;
    31.         int i,n;
    32.         struct sigaction act;
    33.         if(argc != 2)
    34.         {
    35.          fprintf(stderr,"usage : test n ",argv[0]);
    36.              return -1;
    37.         }
    38.         n = atoi(argv[1]);
    39.         
    40.         act.sa_handler = sig_alrm_func;
    41.         sigemptyset(&act.sa_mask);
    42.         act.sa_flags |= 0;//SA_RESTART;
    43.         sigaction(SIGALRM,&act,NULL);
    44.         for(i = 0;i<n;i++)
    45.         {
    46.          if((childpid = fork()) <= 0)
    47.                                 break;
    48.         }
    49.        if(childpid == 0 )
    50.         {
    51.                sleep(50);
    52.         }
    53.         while(r_wait(NULL) >0) ;
    54.         fprintf(stderr," i :%d process ID : %ld, parent ID :%ld child ID : %ld ",
    55.                                         i,(long)getpid(),(long)getppid(),(long)childpid);
    56.         return 0;
    57. }
    1. root@libin:~/program/C/UNP/research# ./test 6
      另外一个端口查看ps -ef ,并向父进程发送SIGALRM信号。
    1. root      8679  7694  0 15:04 pts/0    00:00:00 ./test 6
    2. root      8680  8679  0 15:04 pts/0    00:00:00 ./test 6
    3. root      8681  8679  0 15:04 pts/0    00:00:00 ./test 6
    4. root      8682  8679  0 15:04 pts/0    00:00:00 ./test 6
    5. root      8683  8679  0 15:04 pts/0    00:00:00 ./test 6
    6. root      8684  8679  0 15:04 pts/0    00:00:00 ./test 6
    7. root      8685  8679  0 15:04 pts/0    00:00:00 ./test 6
    8. root@libin:~# kill -SIGALRM 8679
    9. 发送信号SIGALRM后 执行端口显示
    10. catch an alarm signal
    11.  i :6  process ID : 8679, parent ID :7694 child ID : 8685
    12.  
    13. 过了若干秒后,子进程完成自己的生命周期,退出。脱出之前,我们看到,子进程的父进程已经不再是8679.因为8679已经退出了,他们的新父亲是1号进程。
    14. root@libin:~/program/C/UNP/research#  i :0  process ID : 8680, parent ID :1 child ID : 0
    15.  i :1  process ID : 8681, parent ID :1 child ID : 0
    16.  i :2  process ID : 8682, parent ID :1 child ID : 0
    17.  i :3  process ID : 8683, parent ID :1 child ID : 0
    18.  i :4  process ID : 8684, parent ID :1 child ID : 0
    19.  i :5  process ID : 8685, parent ID :1 child ID : 0
        通过前面的例子,我们看到了,如果我们收到信号,中断了系统调用,如果我们不重启系统调用,那么,程序不是按照我们预想的方式运行。
     
        OK,我们将r_wait函数中的注释去掉,我们判断错误码errno,如果errno == EINTR,那么我们重启wait函数,那么,我们可以看到如下情景:
     
    1. root@libin:~/program/C/UNP/research# ./test 6
    在另外一个端口执行ps -ef并且给父进程发送SIGALRM信号
     
    root      8733  7694  0 15:21 pts/0    00:00:00 ./test 6
    root      8734  8733  0 15:21 pts/0    00:00:00 ./test 6
    root      8735  8733  0 15:21 pts/0    00:00:00 ./test 6
    root      8736  8733  0 15:21 pts/0    00:00:00 ./test 6
    root      8737  8733  0 15:21 pts/0    00:00:00 ./test 6
    root      8738  8733  0 15:21 pts/0    00:00:00 ./test 6
    root      8739  8733  0 15:21 pts/0    00:00:00 ./test 6
     
    root@libin:~# kill -SIGALRM 8733
     
    在执行端口,我们看到,程序运行50s后,有如下打印:
    1. catch an alarm signal
    2. may be interrupted by a signal,let wait again
    3.  i :1 process ID : 8735,     parent ID :8733     child ID : 0
    4.  i :0 process ID : 8734,     parent ID :8733     child ID : 0
    5.  i :2 process ID : 8736,     parent ID :8733     child ID : 0
    6.  i :3 process ID : 8737,     parent ID :8733     child ID : 0
    7.  i :4 process ID : 8738,     parent ID :8733     child ID : 0
    8.  i :5 process ID : 8739,     parent ID :8733     child ID : 0
    9.  i :6 process ID : 8733,     parent ID :7694     child ID : 8739
        最后,我们试一下BSD方案,r_wait函数不判断errno,不重启wait,但是act.sa_flags = SA_RESTART.
     
    1. root@libin:~/program/C/UNP/research# ./test 6
    2. catch an alarm signal
    3. i :0 process ID : 8775, parent ID :8774 child ID : 0
    4. i :1 process ID : 8776, parent ID :8774 child ID : 0
    5. i :2 process ID : 8777, parent ID :8774 child ID : 0
    6. i :3 process ID : 8778, parent ID :8774 child ID : 0
    7. i :4 process ID : 8779, parent ID :8774 child ID : 0
    8. i :5 process ID : 8780, parent ID :8774 child ID : 0
    9. i :6 process ID : 8774, parent ID :7694 child ID : 8780
        看到了,情况和sa_flags = 0同时重启系统调用是一样的。
    原文出处:http://blog.chinaunix.net/uid-24774106-id-3065234.html
  • 相关阅读:
    Struts2标签库
    ognl表达式
    Struts2拦截器
    Struts2文件上传与下载
    Swoft2.x 小白学习笔记 (四) --- RPC
    Swoft2.x 小白学习笔记 (三) --- Task、协程
    Swoft2.x 小白学习笔记 (二) --- mysql、redis
    Swoft2.x 小白学习笔记 (一) ---控制器
    Tornado WebSocket简单聊天
    用python实现的21点游戏
  • 原文地址:https://www.cnblogs.com/Mr-Shadow/p/3226977.html
Copyright © 2020-2023  润新知