5

我有一个程序分叉给一个孩子做一些工作,但我现在一次只做一个孩子。我wait()用来等待孩子完成,我是否还需要做任何事情SIGCHLD(例如禁用处理程序)?

在我的情况下,我得到一个值EINTRin errnowhich 导致我认为我需要 mask SIGCHLD

概括地说,这是程序:

  • 阅读论据
  • for(待办工作清单)
  • fork()
  • 如果孩子,execlp()工作计划
  • 如果是父母,wait()让孩子完成
  • 当孩子完成时,父母循环到下一个工作项
4

2 回答 2

8

理论

POSIXSIG_IGN(严格按照 XSI 扩展说明):

如果 SIGCHLD 信号的动作设置为 SIG_IGN,则调用进程的子进程在终止时不应变成僵尸进程。如果调用进程随后等待其子进程,并且该进程没有转化为僵尸进程的未等待子进程,则它将阻塞直到其所有子进程终止,并且 wait()、waitid() 和 waitpid() 将失败并将 errno 设置为 [ECHILD]。

并且在描述中<signal.h>说默认信号处置SIGCHLDSIG_IGN“忽略”(代码“I”)。但是,它的SIG_DFL行为是“忽略”信号;信号永远不会产生。这与SIG_IGN行为不同。

因此,您不必设置信号处理程序,但您不应该获取有关该进程的信息——一旦没有子进程,您应该只会得到一个错误。

示范代码

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;

static void catcher(int signum, siginfo_t *info, void *vp)
{
    sig_num = signum;
    sig_info = *info;
    sig_ctxt = vp;
}

static void set_handler(int signum)
{
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = catcher;
    sigemptyset(&sa.sa_mask);

    if (sigaction(signum, &sa, 0) != 0)
    {
        int errnum = errno;
        fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
        exit(1);
    }
}

static void prt_interrupt(FILE *fp)
{
    if (sig_num != 0)
    {
        fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
        sig_num = 0;
    }
}

static void five_kids(void)
{
    for (int i = 0; i < 5; i++)
    {
        pid_t pid = fork();
        if (pid < 0)
            break;
        else if (pid == 0)
        {
            printf("PID %d - exiting with status %d\n", (int)getpid(), i);
            exit(i);
        }
        else
        {
            int status = 0;
            pid_t corpse = wait(&status);
            printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
            prt_interrupt(stdout);
            fflush(0);
        }
    }
}

int main(void)
{
    printf("SIGCHLD set to SIG_IGN\n");
    signal(SIGCHLD, SIG_IGN);
    five_kids();
    printf("SIGCHLD set to catcher()\n");
    set_handler(SIGCHLD);
    five_kids();
    return(0);
}

fflush(0);调用确保标准输出(特别是)被刷新,如果示例程序的输出通过管道传输到另一个程序,这很重要。

示例输出

示例代码的输出与理论一致——但需要一点解释。

SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4

输出的第一部分与理论完全一致;调用代码除了没有死去的孩子(左)等待外,没有得到关于死去的孩子的信息。

输出的第二部分也与理论一致,但似乎子代没有在父代之前执行,因此父代的第一个wait()没有死去的子代等待,它返回 -1。随后的调用得到了各种孩子,但一个逃跑了(但尸体会被系统清理)。如果使用的代码waitpid()如:pid_t corpse = waitpid(pid, &status, 0);,它会依次等待每个孩子。

鉴于上述关于“忽略”的修改后的评论,其中一些评论可能需要修改。待定——现在没时间了。

Mac OS X 10.8.4,3 GHz Intel Core 2 Duo,GCC 4.6.0,64 位编译:

gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition sigchld.c -o sigchld 

这是对一些现有代码的快速改编,以测试 和 的SIGINT行为pause()。它可能包含一些多余的材料。您不必wait()在信号处理程序中调用或其亲属之一;不过,如果您愿意,您可以这样做。

于 2013-08-26T07:12:29.310 回答
3

如果你不关心 fork children 的退出状态,那么你可以忽略SIGCHLD.

signal(SIGCHLD, SIG_IGN);

如果您确实关心退出状态,则可以安装信号处理程序,并在waitpid()信号处理程序中执行调用以从退出的子进程中获取退出状态值。这个调用应该在一个循环中,直到没有更多退出的孩子。您将-1在第一个参数和WNOHANG最后一个参数中使用以非阻塞方式等待任何孩子。如果waitpid()返回 0,则表示调用成功,但没有孩子等待。

void sigchld_handler (int sig) {
    assert(sig == SIGCHLD);
    int status;
    pid_t child;
    while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
        /*...do something with exited child's status */
    }
}

/*...*/
signal(SIGCHLD, sigchld_handler);
于 2013-08-26T06:31:51.670 回答