【问题标题】:Correct way for waiting for all children in real application实际应用中等待所有孩子的正确方法
【发布时间】:2014-04-13 21:36:06
【问题描述】:

我正在编写一个多线程应用程序(学校项目,修改过河问题)。 我正在使用 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 是传递给程序的参数

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

【问题讨论】:

  • 您可以在退出之前增加子进程的信号量(或注册一个 dtor 函数(在真正退出之前执行))。在主进程中,您将尝试减少它(在进程增加信号量时阻塞信号量)并使用 child_exited_counter。或者只使用信号
  • 你能解释一下你不能使用wait()的原因吗?我问,因为您要求专业技术。
  • 专业技术,函数wait in loop吗?在项目规范中是不要使用主动等待 IPC。
  • 根据需要调用 wait()(或 waitpid())就足够了。

标签: c linux process posix wait


【解决方案1】:

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

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

所以你的选择是:

  1. 通过waitwaitpid 让父块等待它们。
  2. 使用waitpid 和 WNOHANG 选项定期轮询他们
  3. 传送 SIGCHLD 信号并在它们出现时处理它们
  4. 已传递 SIGCHLD 信号并使用 signalfd(在 linux 上)但这仍然需要轮询或使用 select/poll/epoll

由于您拒绝了 1 和 2(以及隐含的 4),因此需要处理信号并使用处理程序。出于心理健康原因,即使不一定是专业性,大多数人会尽可能避免信号,并且如果他们不必这样做,也不会去寻找处理它们的方法,因为:

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

  2. 由于您通过选择处理信号(SIGCHLD)而特意邀请了一个信号(SIGCHLD)进入您的生活,因此您承担了系统调用被中断的后果。由于您使用的是 POSIX 信号量,因此 sem_wait 将特别值得关注。您可以通过在建立处理程序时通过sigaction 调用打开 SA_RESTART 标志来解决大部分问题,但即使使用该标志,仍有许多调用不会自动重新启动。 p>

  3. 多线程和信号都有自己的麻烦。

以下是上述一些问题的粗略但说明性示例:

#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);
}

【讨论】:

    【解决方案2】:
    while ( wait(NULL) > 0 )
    

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

    如果您希望父进程等待N 进程退出,您可以简单地使用这样的循环:

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

    【讨论】:

    • 没有孩子的地方 wait() 应该返回 -1。没有?
    • @user3463614 只有 0 在 C 中是 false
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 2019-12-10
    • 1970-01-01
    • 2010-09-24
    • 2014-11-15
    • 1970-01-01
    相关资源
    最近更新 更多