zoukankan      html  css  js  c++  java
  • 重启系统调用探究 ( 转)

     
         但是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
  • 相关阅读:
    BNU 51002 BQG's Complexity Analysis
    BNU OJ 51003 BQG's Confusing Sequence
    BNU OJ 51000 BQG's Random String
    BNU OJ 50999 BQG's Approaching Deadline
    BNU OJ 50998 BQG's Messy Code
    BNU OJ 50997 BQG's Programming Contest
    CodeForces 609D Gadgets for dollars and pounds
    CodeForces 609C Load Balancing
    CodeForces 609B The Best Gift
    CodeForces 609A USB Flash Drives
  • 原文地址:https://www.cnblogs.com/Mr-Shadow/p/3226977.html
Copyright © 2011-2022 走看看