簡體   English   中英

對 output 消息感到困惑(關於信號處理程序和 sigsuspend)

[英]Confused about the output message (about signal handler and sigsuspend)

為了更好地理解sigsuspend的概念,我做了如下兩處修改,得到了不同的 output 消息,這讓我很困惑。

代碼來自csapp Chapter8 figure 8-42關於sigsuspend

  1. 添加第 10 行printf("Reap child %d\n", pid);
   /* $begin sigsuspend */
   #include "csapp.h"
  
   volatile sig_atomic_t pid;
  
   void sigchld_handler(int s)
   {
       int olderrno = errno;
       pid = Waitpid(-1, NULL, 0);
       printf("Reap child %d\n", pid); //Line 10
       errno = olderrno;
  }
 
  void sigint_handler(int s)
  {
  }
 
  int main(int argc, char **argv)
  {
      sigset_t mask, prev;
 
      Signal(SIGCHLD, sigchld_handler);
      Signal(SIGINT, sigint_handler);
      Sigemptyset(&mask);
      Sigaddset(&mask, SIGCHLD);
 
      while (1) {
          Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
          if (Fork() == 0) /* Child */
              exit(0);
          /* Wait for SIGCHLD to be received */
          pid = 0;
          while (!pid)
              Sigsuspend(&prev);
 
          /* Optionally unblock SIGCHLD */
          Sigprocmask(SIG_SETMASK, &prev, NULL);
 
          /* Do some work after receiving SIGCHLD */
          printf(".");
      }
      exit(0);
  }
  /* $end sigsuspend */

使用kill -9 processID殺死另一個終端中的進程,並得到以下消息,這讓我很困惑,為什么有兩個. 在每個Reap child之前?

Reap child 11880
..Reap child 11881
..Reap child 11882
..Reap child 11883
..Reap child 11884
..Reap child 11885
..Reap child 11886
..Reap child 11887
..Reap child 11888
..Reap child 11889
..Reap child 11890
..Reap child 11891
..Reap child 11892
..Reap child 11893
..Reap child 11894
..Reap child 11895
..Reap child 11896
..Reap child 11897
..Reap child 11898
..Reap child 11899
..Reap child 11900
Killed

然后,添加第 30 行printf("Create child %d\n", getpid());

   /* $begin sigsuspend */
   #include "csapp.h"
  
   volatile sig_atomic_t pid;
  
   void sigchld_handler(int s)
   {
       int olderrno = errno;
       pid = Waitpid(-1, NULL, 0);
      printf("Reap child %d\n", pid);
      errno = olderrno;
  }
 
  void sigint_handler(int s)
  {
  }
 
  int main(int argc, char **argv)
  {
      sigset_t mask, prev;
 
      Signal(SIGCHLD, sigchld_handler);
      Signal(SIGINT, sigint_handler);
      Sigemptyset(&mask);
      Sigaddset(&mask, SIGCHLD);
 
      while (1) {
          Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
          if (Fork() == 0) {/* Child */
              printf("Create child %d\n", getpid()); //Line 30
              exit(0);
          }
          /* Wait for SIGCHLD to be received */
          pid = 0;
          while (!pid)
              Sigsuspend(&prev);
 
          /* Optionally unblock SIGCHLD */
          Sigprocmask(SIG_SETMASK, &prev, NULL);
 
          /* Do some work after receiving SIGCHLD */
          printf(".");
      }
      exit(0);
  }
  /* $end sigsuspend */

使用kill -9 processID殺死另一個終端中的進程,並得到我所想象的消息。

Create child 15080
Reap child 15080
.Create child 15081
.Reap child 15081
.Create child 15082
.Reap child 15082
.Create child 15083
.Reap child 15083
.Create child 15084
.Reap child 15084
.Create child 15085
Killed

為什么只添加一條printf行就會有這樣的差異?

點的問題是緩沖問題。

stdout連接到交互式終端(即 shell)時,默認情況下它是line buffered ,這意味着 output 實際上是在換行符上寫入(刷新)的。

既然你打印

printf(".");

沒有換行符,它保存在要打印的內部stdout緩沖區中,當您打印換行符時(如在sigchld_handler函數中),進程退出或緩沖區變滿。

現在是重要的部分:這個stdout緩沖區由子進程繼承!

因此,當子進程退出時,它將刷新緩沖區並打印一個點。 並且將調用信號處理程序,該處理程序打印一個換行符,該換行符還刷新緩沖區並打印一個點。

您可以通過在打印點后顯式刷新緩沖區來解決此問題:

printf(".");
fflush(stdout);

這樣做之后,當下一個子進程被派生時,緩沖區將是空的。


如果您仔細查看“工作”output,您仍然有兩個點,一個由“創建子”之前的子進程打印 output,另一個在“收割子”之前的父進程中打印 Z78E6221F6393D13566F6393D13566F6393D13566F6393D13566F6393D13566F6

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM