在实际应用中等待所有孩子的正确方法

时间:2014-04-13 21:36:06

标签: c linux process posix wait

我正在编写一个多线程应用程序(学校项目,修改过河问题)。 我正在使用POSIX信号量,共享内存和fork函数。主流程创建了2个流程。然后他们每个人创建N个进程。每个过程代表一个人。我想知道什么是专业技术/最好的方法让父母等到他们所有的孩子,直到他们完成,然后获得他们的退出代码。我不能/不能使用像:

这样的结构
while (wait(NULL) > 0)
    ;
// parent code

使用共享变量的结构都不像

while (1)
    if (num_of_processes == num_of_finished_processes)
        break;
// parent_code

注意:num_of_processes是传递给程序

的参数

我可以以某种方式使用信号量告诉父母:“现在醒来并执行你的代码”。 用一句话。我不想使用主动/循环等待。 谢谢你的任何建议。我只是这个领域的初学者。

2 个答案:

答案 0 :(得分:1)

“专业”的定义可以是弹性的。

基本上有两个原因可以收获你的孩子:避免僵尸在进程表中占用空间并询问孩子的返回码和(可能)基于他们的一些行动。这两个都可以指出,在所有条件相同的情况下,尽可能快地收获它们。

所以你的选择是:

  1. 让父版块使用waitwaitpid等待它们。
  2. 使用waitpid和WNOHANG选项
  3. 定期轮询他们
  4. 交付SIGCHLD信号并在出现时处理
  5. 传递SIGCHLD信号并使用signalfd(在Linux上)但这仍然需要轮询或使用select/poll/epoll
  6. 因为你拒绝了1& 2(并隐式4)留下处理信号和使用处理程序。出于心理健康的原因,如果不一定是专业性,大多数人尽可能地避免信号,如果他们不必这样做,就不去寻找处理它们的方法,因为:

    1. 信号处理程序有自己的约束,主要是您希望尽快进出它们,并且您应该在其中使用有限数量的异步安全函数。所以这通常意味着在处理程序中记录任何内容,并在主程序或专用程序中处理信息。

    2. 由于您已选择处理信号(SIGCHLD),因此您已经承担了系统调用被中断的后果。由于您使用的是POSIX信号量,因此sem_wait会特别受到关注。您可以通过在建立处理程序时通过sigaction调用打开SA_RESTART标志来绕过大多数,但即使有标志,也会有一些不会自动重新启动的调用。

    3. 多线程和信号带来了一系列令人头疼的问题。

    4. 以下是上述一些问题的粗略但有说服力的例子:

      #define  _POSIX_C_SOURCE 200809L
      
      #include <stdio.h>
      #include <unistd.h>
      #include <signal.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/wait.h>
      #include <errno.h>
      
      typedef struct childInfo
      {
          pid_t childPid;
            int childstatus;
      
      } childInfo;
      
      static volatile sig_atomic_t numberOfChildren = 0;
      static volatile sig_atomic_t childrenReaped = 0;
      
      childInfo *childrenTable;
      
      void saveStatus(pid_t pid, int status)
      {
          for (int i = 0; i < numberOfChildren; ++i)
          {
              if (pid == childrenTable[i].childPid)
                  childrenTable[i].childstatus = status;
          }
      }
      
      void printChildrenStatus()
      {
          for (int i = 0; i < numberOfChildren; ++i)
          {
              if (childrenTable[i].childPid != 0)
              {
                  if (WIFEXITED(childrenTable[i].childstatus))
                      printf("PID %d exited normally.  "
                             "Exit status: %d\n",
                              childrenTable[i].childPid, WEXITSTATUS(childrenTable[i].childstatus));
                  else
                      if (WIFSTOPPED(childrenTable[i].childstatus))
                          printf("PID %d was stopped by %d\n",
                                 childrenTable[i].childPid,
                                 WSTOPSIG(childrenTable[i].childstatus));
                      else
                          if (WIFSIGNALED(childrenTable[i].childstatus))
                              printf("PID %d exited due to signal %d\n.",
                                     childrenTable[i].childPid,
                                     WTERMSIG(childrenTable[i].childstatus));
      
                  childrenTable[i].childPid = 0;
                  childrenReaped++;
              }
          }
      }
      
      void childHandler(int signum)
      {
            int childstatus;
          pid_t childpid;
      
          while ((childpid = waitpid( -1, &childstatus, WNOHANG)) > 0)
              saveStatus(childpid, childstatus);
      }
      
      int main(int argc, char *argv[])
      {
          if (argc > 1)
             numberOfChildren = atoi(argv[1]);
          else
          {
              printf("must enter num of children to create...");
              exit(1);
          }
      
          childrenTable = calloc(numberOfChildren, sizeof(childInfo));
      
          struct sigaction sa;
      
          sa.sa_handler = childHandler;
          sigemptyset(&sa.sa_mask);
          sa.sa_flags = SA_RESTART; // Restart functions, particularly your parent's
                                    // sem_wait if interrupted by the handler
          if (sigaction(SIGINT, &sa, NULL) == -1)
          {
              perror("sigaction");
              exit(1);
          }
      
          for (int i = 0; i < numberOfChildren; ++i)
          {
             pid_t pid = fork();
      
             if (pid)
                 childrenTable[i].childPid = pid;
             else
             {
                 sleep(i);
                 exit(0);
             }
          }
      
          while(numberOfChildren - childrenReaped)
          {
              pause();
              printChildrenStatus();
          }
      
          return(0);
      }
      

答案 1 :(得分:0)

while ( wait(NULL) > 0 )

是一个infine循环,因为wait(2)的返回值是已终止子进程的pid,即!= 0,并且不会更改。

如果您希望父进程等待N进程退出,您只需使用这样的循环:

for (i = 0; i < N; ++i)
    wait(NULL);