Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
439 views
in Technique[技术] by (71.8m points)

c - Do I need to do anything with a SIGCHLD handler if I am just using wait() to wait for 1 child to finish at a time?

I've got a program that is forking to a child to do some work, but I am only doing one child at a time at this time. I am using wait() to wait for the child to finish, do I need to do anything with SIGCHLD as well (such as disable the handler)?

In my situation I am getting a value of EINTR in errno which leads me to think that I need to mask SIGCHLD.

In broad strokes, this is the program:

  • read arguments
  • for(list of work to do)
  • fork()
  • if child, execlp() to work program
  • if parent, wait() for child to finish
  • when child finishes, parent loops to next work item
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Theory

POSIX says about SIG_IGN (strictly under the XSI extension note):

If the action for the SIGCHLD signal is set to SIG_IGN, child processes of the calling processes shall not be transformed into zombie processes when they terminate. If the calling process subsequently waits for its children, and the process has no unwaited-for children that were transformed into zombie processes, it shall block until all of its children terminate, and wait(), waitid(), and waitpid() shall fail and set errno to [ECHILD].

And in the description of <signal.h> says that the default signal disposition for SIGCHLD is SIG_IGN 'ignore' (code 'I'). However, it is the SIG_DFL behaviour is to 'ignore' the signal; the signal is never generated. This is different from the SIG_IGN behaviour.

So, you don't have to set a signal handler, but you shouldn't get information about the process back — you should just get an error once there are no children left.

Demonstration Code

#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)
", errnum, strerror(errnum));
        exit(1);
    }
}

static void prt_interrupt(FILE *fp)
{
    if (sig_num != 0)
    {
        fprintf(fp, "Signal %d from PID %d
", 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
", (int)getpid(), i);
            exit(i);
        }
        else
        {
            int status = 0;
            pid_t corpse = wait(&status);
            printf("Child: %d; Corpse: %d; Status = 0x%.4X
", pid, corpse, (status & 0xFFFF));
            prt_interrupt(stdout);
            fflush(0);
        }
    }
}

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

The fflush(0); call ensures standard output (in particular) is flushed, which matters if the output of the sample program is piped to another program.

Example Output

The output from the example code agrees with the theory — but requires a little interpretation.

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

The first section of the output agrees exactly with the theory; the calling code gets no information about the dead children except that there are no dead children (left) to wait for.

The second section of the output agrees with the theory too, but it appears that the children aren't executing before the parent, so the first wait() from the parent has no dead children to wait for, and it returns -1. Subsequent calls get the various children, but one escapes unwaited for (but the corpse will be cleaned up by the system). If the code used waitpid() such as: pid_t corpse = waitpid(pid, &status, 0);, it would wait for each child in turn.

Some of this commentary may need revision in the light of the modified comments about 'ignore' above. TBD — out of time right now.

Mac OS X 10.8.4 on 3 GHz Intel Core 2 Duo, GCC 4.6.0, 64-bit compilation:

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

This was a quick adaptation of some existing code to test the behaviour of SIGINT and pause(). It may have some superfluous material in it. You do not have to call wait() or one of its relatives in the signal handler; you may do so if you wish, though.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...