重啓系統調用探究 ( 轉)

 
     可是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 \n");
  15.                 }
  16.                 else*/
  17.                 {
  18.                         break;
  19.                 }
  20.         }
  21.         return ret;
  22. }
  23. void sig_alrm_func()
  24. {
  25.     printf("catch an alarm signal\n");
  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 \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,\t parent ID :%ld \tchild ID : %ld\n",
  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
相關文章
相關標籤/搜索