<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [apue] 等待子進程的那些事兒

      前言

      談到等待子進程,首先想到的就是 SIGCHLD 信號與 wait 函數族,本文試圖厘清二者的方方面面,以及組合使用時可能的坑。

      單獨使用 SIGCHLD 的場景

      使用 signal 捕獲信號

      下面是一段典型的代碼片段:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 
       4 #define CLD_NUM 2
       5 static void sig_cld (int signo)
       6 {
       7     pid_t pid = 0; 
       8     int status = 0; 
       9     printf ("SIGCHLD received\n"); 
      10     if (signal (SIGCHLD, sig_cld) == SIG_ERR)
      11         perror ("signal error"); 
      12     if ((pid = wait (&status)) < 0)
      13         perror ("wait(in signal) error"); 
      14     printf ("pid (wait in signal) = %d\n", pid); 
      15 }
      16 
      17 int main ()
      18 {
      19     pid_t pid = 0; 
      20     __sighandler_t ret = signal (SIGCHLD, sig_cld);
      21     if (ret == SIG_ERR)
      22         perror ("signal error"); 
      23     else 
      24         printf ("old handler %x\n", ret); 
      25 
      26     for (int i=0; i<CLD_NUM; ++ i)
      27     {
      28         if ((pid = fork ()) < 0)
      29             perror ("fork error"); 
      30         else if (pid == 0) 
      31         {
      32             sleep (3); 
      33             printf ("child %u exit\n", getpid ()); 
      34             _exit (0); 
      35         }
      36 
      37         sleep (1); 
      38     }
      39 
      40     for (int i=0; i<CLD_NUM; ++ i)
      41     {
      42         pause (); 
      43         printf ("wake up by signal %d\n", i); 
      44     }
      45 
      46     printf ("parent exit\n"); 
      47     return 0; 
      48 }

       父進程啟動了兩個子進程,在 SIGCHLD 信號處理器中調用 wait 等待已結束的子進程,回收進程信息,防止產生僵尸進程 (zombie)。上面的代碼會有如下的輸出:

      old handler 0
      child 28542 exit
      SIGCLD received
      pid (wait in signal) = 28542
      wake up by signal 0
      child 28543 exit
      SIGCLD received
      pid (wait in signal) = 28543
      wake up by signal 1
      parent exit
      

      使用 sigaction 捕獲信號

      當然捕獲 SIGCHLD 也可以使用 sigaction 函數:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 
       4 #define CLD_NUM 2
       5 static void sig_cld (int signo, siginfo_t *info, void* param)
       6 {
       7     int status = 0; 
       8     if (signo == SIGCHLD)
       9     {
      10         if (info->si_code == CLD_EXITED ||
      11                 info->si_code == CLD_KILLED || 
      12                 info->si_code == CLD_DUMPED)
      13         {
      14             //printf ("child %d die\n", info->si_pid); 
      15             if (waitpid (info->si_pid, &status, 0) < 0)
      16                 perror ("wait(in signal) error"); 
      17             printf ("pid (wait in signal) = %d\n", info->si_pid); 
      18         }
      19         else 
      20         {
      21             printf ("unknown signal code %d\n", info->si_code); 
      22         }
      23     }
      24 }
      25 
      26 int main ()
      27 {
      28     pid_t pid = 0; 
      29     struct sigaction act; 
      30     sigemptyset (&act.sa_mask); 
      31     act.sa_sigaction = sig_cld; 
      32     act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; 
      33     int ret = sigaction (SIGCHLD, &act, 0); 
      34     if (ret == -1)
      35         perror ("sigaction error"); 
      36 
      37     for (int i=0; i<CLD_NUM; ++ i)
      38     {
      39         if ((pid = fork ()) < 0)
      40             perror ("fork error"); 
      41         else if (pid == 0) 
      42         {
      43             sleep (3); 
      44             printf ("child %u exit\n", getpid ()); 
      45             _exit (0); 
      46         }
      47 
      48         sleep (1); 
      49     }
      50 
      51     for (int i=0; i<CLD_NUM; ++ i)
      52     {
      53         pause (); 
      54         printf ("wake up by signal %d\n", i); 
      55     }
      56 
      57     printf ("parent exit\n"); 
      58     return 0; 
      59 }

      輸出是一樣的。關于 signal 與 sigaction 的區別,有以下幾點:

      • 使用 sigaction 可以避免重新安裝信號處理器的問題;
      • 使用 sigaction 可以在 wait 之前得知是哪個子進程結束了。這是通過指定 SA_SIGINFO 標志位,并提供帶 siginfo_t 參數的信號處理器來實現的 (info->si_pid 就是結束進程的進程號);
      • 使用 sigaction 可以獲取除子進程結束以外的狀態變更通知,例如掛起、繼續,默認接收相應通知,除非指定 SA_NOCLDSTOP 標志。而對于 signal 而言,沒有辦法不接收子進程非結束狀態的通知 (此時調用 wait 可能會卡死);
      • 使用 sigaction 可以自動 wait 已結束的子進程,只要指定 SA_NOCLDWAIT 標志即可。此時在信號處理器中不用再調用 wait 函數了。

      當使用 SA_NOCLDWAIT 標志位時,使用 systemtap 還是可以觀察到子進程還是向父進程發送了 SIGCHLD 信號的:

      30049    cldsig           30048 cldsig           17     SIGCHLD         
      30050    cldsig           30048 cldsig           17     SIGCHLD   
      

      很有可能是系統內部自動 wait 了相關子進程。另外在使用 SA_NOCLDWAIT 時,可以不指定信號處理器,此時 sa_sigaction 字段可以設置為 SIG_DFL。關于 SIGCHLD 信號,有以下幾點需要注意:

      • 如果在注冊信號之前,就已經有已結束但未等待的子進程存在,則事件不會被觸發;
      • 可以為 SIGCHLD 注冊一個處理器,也可以忽略該信號 (SIG_IGN),忽略時系統自動回收已結束的子進程;

      當正常捕獲 SIGCHLD 時,使用 systemtap 是可以觀察到子進程向父進程發送的 SIGCHLD 信號的:

      29877    cldsig           29876 cldsig           17     SIGCHLD         
      29878    cldsig           29876 cldsig           17     SIGCHLD         
      29876    cldsig           27771 bash             17     SIGCHLD  
      

      當忽略 SIGCHLD 時,是觀察不到的,只能看到父進程結束時向 bash 發送的 SIGCHLD 信號:

      29893    cldsig           27771 bash             17     SIGCHLD  
      

      這里注意一下二者在細節處的一點區別。

      •  還有一個 SIGCLD 信號 (看清楚,只差了一個字母),在大多數 unix like 系統中與 SIGCHLD 表現一致,在某些古老的 unix 系統上,可能有獨特的表現需要注意,這方面請參考 apue 第十章第七節

      在我的環境 (CentOS 6.7) 該信號被定義為 SIGCHLD,因此是完全等價的。

      屏蔽信號

      關于使用信號等待子進程,最后需要說的一點就是信號的競爭行為,對上面的例子稍加修改,就可以演示一下:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 
       4 #define CLD_NUM 2
       5 void pid_remove (pid_t pid)
       6 {
       7     printf ("remove pid %u\n", pid); 
       8 }
       9 void pid_add (pid_t pid)
      10 {
      11     printf ("add pid %u\n", pid); 
      12 }
      13 
      14 static void sig_cld (int signo)
      15 {
      16     pid_t pid = 0; 
      17     int status = 0; 
      18     printf ("SIGCHLD received\n"); 
      19     if (signal (SIGCHLD, sig_cld) == SIG_ERR)
      20         perror ("signal error"); 
      21     if ((pid = wait (&status)) < 0)
      22         perror ("wait(in signal) error"); 
      23     printf ("pid (wait in signal) = %d\n", pid); 
      24     pid_remove (pid); 
      25 }
      26 
      27 int main ()
      28 {
      29     pid_t pid = 0; 
      30     __sighandler_t ret = signal (SIGCHLD, sig_cld);
      31     if (ret == SIG_ERR)
      32         perror ("signal error"); 
      33     else 
      34         printf ("old handler %x\n", ret); 
      35 
      36     for (int i=0; i<CLD_NUM; ++ i)
      37     {
      38         if ((pid = fork ()) < 0)
      39             perror ("fork error"); 
      40         else if (pid == 0) 
      41         {
      42             //sleep (3); 
      43             printf ("child %u exit\n", getpid ()); 
      44             _exit (0); 
      45         }
      46 
      47         sleep (1);
      48         pid_add (pid);  
      49     }
      50 
      51     sleep (1); 
      52     printf ("parent exit\n"); 
      53     return 0; 
      54 }

      父進程在啟動子進程后需要將它的信息通過 pid_add 添加到某種數據結構中,當收到 SIGCHLD 信號后,又通過 pid_remove 將它從這個數據結構中移出。在上面的例子中,子進程一啟動就退出了,快到甚至父進程還沒有來得及執行 pid_add 就先執行了 pid_remove,這很容易導致潛在的問題。(注意,為了能更好的呈現信號競爭的問題,這里故意在父進程 sleep 之后調用 pid_add),執行結果如下:

      old handler 0
      child 31213 exit
      SIGCLD received
      pid (wait in signal) = 31213
      remove pid 31213
      add pid 31213
      child 31214 exit
      SIGCLD received
      pid (wait in signal) = 31214
      remove pid 31214
      add pid 31214
      parent exit
      

      可以看到,remove 總是在 add 之前執行。而解決方案也很直接,就是在 pid_add 完成之前,我們需要屏蔽 SIGCHLD 信號:

       1     for (int i=0; i<CLD_NUM; ++ i)
       2     {
       3         sigset_t mask; 
       4         sigemptyset(&mask);
       5         sigaddset(&mask, SIGCHLD);
       6         sigprocmask(SIG_BLOCK, &mask, NULL);
       7         if ((pid = fork ()) < 0)
       8             perror ("fork error"); 
       9         else if (pid == 0) 
      10         {
      11             sigprocmask(SIG_UNBLOCK, &mask, NULL);
      12             //sleep (3); 
      13             printf ("child %u exit\n", getpid ()); 
      14             _exit (0); 
      15         }
      16 
      17         sleep (1);
      18         pid_add (pid);  
      19         sigprocmask(SIG_UNBLOCK, &mask, NULL);
      20     }

      這里用到了 sigprocmask 去屏蔽以及解除某種信號的屏蔽。新的代碼運行結果如下:

      old handler 0
      child 31246 exit
      add pid 31246
      SIGCLD received
      pid (wait in signal) = 31246
      remove pid 31246
      child 31247 exit
      SIGCLD received
      pid (wait in signal) = 31247
      remove pid 31247
      add pid 31247
      parent exit
      

      可以看到一切正常了,add 這次位于 remove 之前。總結一下,使用 SIGCHLD 信號適合異步等待子進程的場景,并且通常搭配 wait 來回收子進程。

      單獨使用 wait 函數族的場景

      典型代碼如下:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 
       4 #define CLD_NUM 2
       5 int main ()
       6 {
       7     pid_t pid = 0; 
       8     for (int i=0; i<CLD_NUM; ++ i)
       9     {
      10         if ((pid = fork ()) < 0)
      11             perror ("fork error"); 
      12         else if (pid == 0) 
      13         {
      14             sleep (3); 
      15             printf ("child %u exit\n", getpid ()); 
      16             _exit (0); 
      17         }
      18 
      19         sleep (1); 
      20     }
      21 
      22     int status = 0; 
      23     for (int i=0; i<CLD_NUM; ++ i)
      24     {
      25         if ((pid = wait (&status)) < 0)
      26             perror ("wait error"); 
      27 
      28         printf ("pid = %d\n", pid); 
      29     }
      30 
      31     printf ("parent exit\n"); 
      32     return 0; 
      33 }

      與之前場景不同的是,這里父進程同步等待啟動的子進程結束。上面的代碼會有如下輸出:

      child 28583 exit
      child 28584 exit
      pid = 28583
      pid = 28584
      parent exit
      

      關于wait函數族,需要注意以下幾點:

      • wait 用于等待任何一個子進程,相當于 waitpid (-1, status, 0);  當沒有任何子進程存在時,返回 -1,errno 設置為 ECHILD;
      • waitpid 相對于 wait 的優勢在于:
        • 可以指定子進程 (組) 來等待;
        • 可以捕獲子進程除結束以外的其它狀態變更通知,如掛起 (WUNTRACED)、繼續 (WCONTINUED) 等;
        • 可以不阻塞的測試某個子進程是否已結束 (WNOHANG);
      • wait 函數族可被信號中斷,此時返回 -1,errno 設置為 EINTR,必要時需要重啟 wait;

      總結一下,使用 wait 函數族適合同步等待子進程,例如某種命令執行器進程,通常配合 waitpid 來回收子進程。

      混合使用同步 wait 與異步 wait 函數族的場景

      其實前面已經提到 SIGCHLD 要搭配 wait 使用,但那是異步使用 wait 的單一場景,而這里講的混合,是指同時在信號處理器與執行流程中使用 wait。例如 bash,它除了在主線程中同步等待前臺正在運行的子進程,還必需在信號處理器中異步接收后臺運行子進程的狀態反饋,這樣就不得不混合使用 wait。同步等待某個子進程一般使用 waitpid,而在信號處理器中一般使用 wait,典型的代碼如下所示:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 #include <errno.h> 
       4 
       5 #define CLD_NUM 2
       6 
       7 static void sig_cld (int signo)
       8 {
       9     pid_t pid = 0; 
      10     int status = 0; 
      11     printf ("SIGCLD received\n"); 
      12     if (signal (SIGCLD, sig_cld) == SIG_ERR)
      13         perror ("signal error"); 
      14 
      15     if ((pid = wait (&status)) < 0)
      16         perror ("wait(in signal) error"); 
      17     else
      18         printf ("pid (wait in signal) = %d\n", pid); 
      19 }
      20 
      21 int main ()
      22 {
      23     pid_t pid = 0; 
      24     __sighandler_t ret = signal (SIGCLD, sig_cld);
      25     if (ret == SIG_ERR)
      26         perror ("signal error"); 
      27     else 
      28         printf ("old handler %x\n", ret); 
      29 
      30     for (int i=0; i<CLD_NUM; ++ i)
      31     {
      32         if ((pid = fork ()) < 0)
      33             perror ("fork error"); 
      34         else if (pid == 0) 
      35         {
      36             if (i % 2 == 0) { 
      37                 // simulate background
      38                 sleep (3); 
      39             }
      40             else {
      41                 // simulate foreground
      42                 sleep (4); 
      43             }
      44 
      45             printf ("child %u exit\n", getpid ()); 
      46             _exit (0); 
      47         }
      48 
      49         sleep (1); 
      50     }
      51 
      52     int status = 0; 
      53     printf ("before wait pid %u\n", pid); 
      54     if (waitpid (pid, &status, 0) < 0)
      55         printf ("wait %u error %d\n", pid, errno); 
      56     else
      57         printf ("wait child pid = %d\n", pid); 
      58 
      59     sleep (2);
      60     printf ("parent exit\n"); 
      61     return 0; 
      62 }

      父進程啟動兩個子進程,第一個休眠 3 秒后退出,第二個休眠 4 秒后退出,由于父進程同步等待的是第二個子進程,因此第二個進程模擬前臺進程,第一個進程模擬后臺進程。運行輸出如下:

      old handler 0
      before wait pid 2481
      child 2480 exit
      SIGCLD received
      pid (wait in signal) = 2480
      wait 2481 error 4
      child 2481 exit
      SIGCLD received
      pid (wait in signal) = 2481
      parent exit
      

      此時同步等待的 waitpid 被信號中斷了 (EINTR),此種情況下,我們需要重啟 waitpid:

       1     int status = 0; 
       2     while (1) { 
       3         printf ("before wait pid %u\n", pid); 
       4         if (waitpid (pid, &status, 0) < 0)
       5         {
       6             int err = errno; 
       7             printf ("wait %u error %d\n", pid, err); 
       8             if (err == EINTR)
       9                 continue; 
      10         }
      11         else
      12             printf ("wait child pid = %d\n", pid); 
      13 
      14         break; 
      15     }

      新的代碼輸出如下:

      old handler 0
      before wait pid 2513
      child 2512 exit
      SIGCLD received
      pid (wait in signal) = 2512
      wait 2513 error 4
      before wait pid 2513
      child 2513 exit
      SIGCLD received
      wait(in signal) error: No child processes
      wait child pid = 2513
      parent exit
      

      可以看到兩個進程退出時,都收到了 SIGCHLD 信號,只是前臺進程被 waitpid 優先等待到了,所以信號處理器中的 wait 返回的 ECHILD 錯誤。但是如果還有其它子進程在運行,信號處理器里的 wait 會卡死。

      忽略信號

      之前提到,可以使用 SIG_IGN 來自動回收子進程,這里試一下使用 SIG_IGN 來代替 sig_cld,看看有什么改觀:

      old handler 0
      before wait pid 2557
      child 2556 exit
      child 2557 exit
      wait 2557 error 10
      parent exit
      

      同樣的,兩個子進程都走了忽略信號,而同步等待的 waitpid 因沒有進程可等返回了 ECHILD。因為 waitpid 是指定進程等待的,所以即使還有其它子進程存在,這個也會返回錯誤,不會卡死在那里。相比上面的方法,似乎好了一點,但是因為我們沒有安裝處理器,所以無從得知哪個后臺進程結束了,這并不是我們想到的結果。

      使用 sigaction

      之前提到,可以使用 sigaction 代替 signal 以獲取更多的控制,我們看看換新的方式捕獲信號,會不會有一些改變,新的代碼邏輯如下:

       1 #include "../apue.h" 
       2 #include <sys/wait.h> 
       3 #include <errno.h> 
       4 
       5 #define CLD_NUM 2
       6 
       7 static void sig_cld (int signo, siginfo_t *info, void* param)
       8 {
       9     int status = 0; 
      10     if (signo == SIGCHLD)
      11     {
      12         if (info->si_code == CLD_EXITED ||
      13                 info->si_code == CLD_KILLED || 
      14                 info->si_code == CLD_DUMPED)
      15         {
      16             if (waitpid (info->si_pid, &status, 0) < 0)
      17                 err_ret ("wait(in signal) %u error", info->si_pid); 
      18             else 
      19                 printf ("pid (wait in signal) = %d\n", info->si_pid); 
      20         }
      21         else 
      22         {
      23             printf ("unknown signal code %d\n", info->si_code); 
      24         }
      25     }
      26 }
      27 
      28 int main ()
      29 {
      30     pid_t pid = 0; 
      31     struct sigaction act; 
      32     sigemptyset (&act.sa_mask); 
      33     act.sa_sigaction = sig_cld; 
      34     act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; 
      35     int ret = sigaction (SIGCHLD, &act, 0); 
      36     if (ret == -1)
      37         perror ("sigaction error"); 
      38 
      39     for (int i=0; i<CLD_NUM; ++ i)
      40     {
      41         if ((pid = fork ()) < 0)
      42             perror ("fork error"); 
      43         else if (pid == 0) 
      44         {
      45             if (i % 2 == 0) { 
      46                 // simulate background
      47                 sleep (3); 
      48             }
      49             else {
      50                 // simulate foreground
      51                 sleep (4); 
      52             }
      53 
      54             printf ("child %u exit\n", getpid ()); 
      55             _exit (0); 
      56         }
      57 
      58         sleep (1); 
      59     }
      60 
      61     int status = 0; 
      62     while (1) { 
      63         printf ("before wait pid %u\n", pid); 
      64         if (waitpid (pid, &status, 0) < 0)
      65         {
      66             int err = errno; 
      67             printf ("wait %u error %d\n", pid, err); 
      68             if (err == EINTR)
      69                 continue; 
      70         }
      71         else
      72             printf ("wait child pid = %d\n", pid); 
      73 
      74         break; 
      75     }
      76 
      77     sleep (2);
      78     printf ("parent exit\n"); 
      79     return 0; 
      80 }

      運行輸出如下:

      before wait pid 2585
      child 2584 exit
      pid (wait in signal) = 2584
      wait 2585 error 4
      before wait pid 2585
      child 2585 exit
      wait(in signal) 2585 error: No child processes
      wait child pid = 2585
      parent exit
      

      結果與使用 signal 很相似,但是因為在信號處理器中我們能明確的知道是哪個子進程終結了,使用的是 waitpid 而不是 wait,所以即使還有其它子進程在運行,也不會在信號處理器的 waitpid 中卡住。因此結論是無論使用 signal 還是 sigaction,同步等待的 waitpid 總比 SIGCHLD 信號處理器中的 wait(xxx) 具有更高的優先級。

      當然,這個前提是在父進程同步 waitpid 之前子進程還沒有結束;如果要等待的子進程先結束了,SIGCHLD 當然先被執行,這種情況下,建議先使用 sigprocmask 屏蔽 SIGCHLD 信號,然后在 waitpid 之前解除屏蔽。雖然不能保證完全解決信號競爭的問題,也能極大的緩解此種情況。退一步講,假如出現了信號競爭導致同步等待的 waitpid 返回 ECHILD,我們也能從這些錯誤碼中得知發生的事情,不會出現卡死的情況。出于好奇,我們看一下改使用 SIG_IGN 后的運行效果:

      before wait pid 2613
      child 2612 exit
      child 2613 exit
      wait 2613 error 10
      parent exit
      

      與使用 signal 時并無二致,仍然是忽略信號占了上風。因此結論是無論使用 signal 還是 sigaction,當忽略 SIGCHLD 信號時,信號優先于 wait 被忽略。出于同樣的原因,這種方式我們并不采納。

      使用 SA_NOCLDWAIT

      之前提到,sigaction還有一種高級的忽略 SIGCHLD 的方式,即指定 SA_NOCLDWAIT 標志位,同時給信號處理器指定 SIG_DFL,這種情況下,我們看看輸出會有什么變化:

      before wait pid 2719
      child 2718 exit
      child 2719 exit
      wait 2719 error 10
      parent exit
      

      可以看到,與使用 SIG_IGN 并無二致。我們可以為 SIGCHLD 提供一個處理器,雖然在此信號處理器中無需再次等待子進程,但是我們擁有了獲取子進程信息的能力,相對而言,比 SIG_IGN 更有用一些。新的輸出如下:

      before wait pid 2737
      child 2736 exit
      pid (auto wait in signal) = 2736
      wait 2737 error 4
      before wait pid 2737
      child 2737 exit
      pid (auto wait in signal) = 2737
      wait 2737 error 10
      parent exit
      

      可以看到,同步 waitpid 仍然返回 ECHILD,顯然是信號更具有優先級。好了,事情至此就全明了了,對于混合使用同步與異步 wait 的應用來說,最佳的方法應該是同步 waitpid 等待前臺進程,使用sigaction 注冊 SIGCHLD 信號處理器異步等待后臺進程,且不設置 SA_NOCLDWAIT 標志位。在處理器中也應使用 waitpid 等待子進程,如返回 ECHILD 錯誤,證明該子進程是前臺進程,已經被同步 wait 掉了,不需要任何處理;否則作為后臺進程處理。

      后記

      說了這么一大堆,可能有的人會說了,我又不需要寫一個 shell,需要用到這么復雜的知識嗎? 確實,沒有多少人會有機會寫一個 shell,但是并非只有 shell 才有混合使用同步、異步等待子進程的場景,考慮下面這個場景:

       1 #include "../apue.h" 
       2 #include <unistd.h> 
       3 #include <sys/wait.h> 
       4 
       5 #define PAGER "${PAGER:-more}"
       6 
       7 #define USE_SIG 2
       8 static void sig_cld (int signo)
       9 {
      10     pid_t pid = 0; 
      11     int status = 0; 
      12     printf ("SIGCLD received\n"); 
      13     if (signal (SIGCLD, sig_cld) == SIG_ERR)
      14         perror ("signal error"); 
      15 
      16     if ((pid = wait (&status)) < 0)
      17         perror ("wait(in signal) error"); 
      18 
      19     printf ("pid (wait in signal) = %d\n", pid); 
      20 }
      21 
      22 void install_handler (__sighandler_t h)
      23 {
      24     __sighandler_t ret = signal (SIGCLD, h);
      25     if (ret == SIG_ERR)
      26         perror ("signal error"); 
      27     else 
      28         printf ("old handler %x\n", ret); 
      29 }
      30 
      31 int main (int argc, char *argv[])
      32 {
      33     int n = 0; 
      34 #if USE_SIG == 1
      35     install_handler (sig_cld); 
      36 #elif USE_SIG == 2
      37     install_handler (SIG_IGN); 
      38 #endif
      39 
      40     char line[MAXLINE] = { 0 }; 
      41     FILE *fpin = NULL, *fpout = NULL; 
      42     if (argc != 2)
      43         err_quit ("usage: ppage <pathname>"); 
      44 
      45     fpin = fopen (argv[1], "r"); 
      46     if (fpin == NULL)
      47         err_sys ("can't open %s", argv[1]); 
      48 
      49     fpout = popen (PAGER, "w"); 
      50     if (fpout == NULL)
      51         err_sys ("popen %s error", PAGER); 
      52 
      53     while (fgets (line, MAXLINE, fpin) != NULL) { 
      54         if (fputs (line, fpout) == EOF)
      55             err_sys ("fputs error to pipe"); 
      56     }
      57 
      58     if (ferror (fpin))
      59         err_sys ("fgets error"); 
      60 
      61     int ret = pclose(fpout); 
      62     if (ret == -1)
      63         err_sys ("pclose error"); 
      64     else 
      65         printf ("worker return %d\n", ret); 
      66 
      67     return 0; 
      68 }

      程序運行后打開參數指定的文件,讀取并將它通過管道傳遞給 more 命令。隨后通過 pclose 等待 more 命令結束。這期間為了保證其它子進程 (假設存在) 能正常回收,使用 SIG_IGN 注冊了 SIGCHLD 信號。運行程序,退出 more 后有如下輸出:

      pclose error: No child processes
      

      pclose 失敗了,為什么呢?答案就是前面說過的,pclose 內部存在著一個隱式的 waitpid 在同步等待 more 子進程,而此時 SIGCHLD 被注冊為忽略取得了優先權,導致 waitpid 失敗從而導致 pclose 返回錯誤。可見,當程序中存在 pclose、system 等隱式 wait 調用時,如果同時需要 SIGCHLD 信號處理,則一定不能:

      • 注冊為忽略 SIG_IGN;
      • 通過 sigaction 注冊并設置 SA_NOCLDWAIT 標志位;

      否則相應的調用會失敗。順便說一下,之前發現同步等待的 waitpid 沒有被中斷的情況只在忽略信號的時候產生,而前面也證明了忽略信號時,系統壓根不產生 SIGCHLD 信號,這兩者似乎到現在是對上了……

      下載

      場景 1&2 測試代碼

      場景3 測試代碼

      使用popen的場景

      完整的shell示例

      posted @ 2019-07-08 15:13  goodcitizen  閱讀(1019)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 99久久er热在这里只有精品99| 国产日韩一区二区在线看| 亚洲精品天堂成人片AV在线播放| 亚洲成av人片无码迅雷下载| 日韩不卡一区二区三区四区| 久久在线视频免费观看| 日本亚洲一区二区精品久久| 久久婷婷五月综合色国产免费观看 | 亚洲国产精品久久久天堂麻豆宅男| 亚洲国产欧美在线观看片| 91久久性奴调教国产免费| 国产成人亚洲日韩欧美| 永久免费av网站可以直接看的 | 国产精品久久久久久爽爽爽| 国产视频有码字幕一区二区| 国产精品中文一区二区| 色色97| 亚洲无av在线中文字幕| 麻花传媒在线观看免费| 夜夜爽77777妓女免费看| 美女裸体黄网站18禁止免费下载| 惠来县| 最新亚洲av日韩av二区| 亚洲精品成人片在线观看精品字幕 | 亚洲AV成人无码久久精品| 婷婷久久香蕉五月综合加勒比| 少妇人妻精品无码专区视频| 国产精品白浆在线观看免费| 国产极品美女高潮抽搐免费网站| 人妻系列无码专区免费| 老河口市| 大尺度国产一区二区视频| 一区二区三区国产亚洲网站| 久热这里有精品视频在线| 亚洲精品一区二区动漫| 铜陵市| 亚洲码欧洲码一二三四五| 亚洲精品岛国片在线观看| 粉嫩一区二区三区精品视频| 亚洲高清最新AV网站| 午夜国产精品福利一二|