1

I have this c code

/* SIGCHLD handler. */
static void sigchld_hdl (int sig)
{
    /* Wait for all dead processes.
     * We use a non-blocking call to be sure this signal handler will not
     * block if a child was cleaned up in another part of the program. */
     //printf("Inside handler:  \n");
    while (waitpid(-1, NULL, WNOHANG) > 0) {
        printf("reaped\n");
    }
     //printf("Outside handler:  \n");

}

int main (int argc, char *argv[])
{
    struct sigaction act;
    int i;

    memset (&act, 0, sizeof(act));
    act.sa_handler = sigchld_hdl;

    if (sigaction(SIGCHLD, &act, 0)) {
        perror ("sigaction");
        return 1;
    }

    /* Make some children. */
    for (i = 0; i < 5; i++) {
        switch (fork()) {
            case -1:
                perror ("fork");
                return 1;
            case 0:
                 exit(0);
                return 0;
            default:
                //printf("inside parent\n");
                printf("Created child %d\n", i);

        }
    }

    /* Wait until we get a sleep() call that is not interrupted by a signal. */
    while (sleep(1)) {
        printf("inside parent while loop\n");
    }

    return 0;
}

I am calling fork 5 times, so I expect total 5 child process and 1 parent process. When I run this code, I get this output

Created child 0
Created child 1
reaped
reaped
Created child 2
Created child 3
Created child 3
reaped
reaped
Created child 4
reaped
inside parent while loop

I can't quite figure out why I am seeing child3 twice. There should not be any duplication with regards to i as i is incrementing in every iteration.

Can somebody explain why I am seeing child3 twice?

Max
  • 9,100
  • 25
  • 72
  • 109
  • 1
    I can't reproduce it. You shouldn't call `printf()` in a signal handler, maybe that's the problem. – Barmar Apr 25 '17 at 02:07
  • @Barmar why shouldn't I call printf in handler. You have to run it a few time to reproduce. – Max Apr 25 '17 at 02:09
  • 1
    It's not async-safe. http://stackoverflow.com/questions/16891019/how-to-avoid-using-printf-in-a-signal-handler – Barmar Apr 25 '17 at 02:09
  • I ran it about 10 times – Barmar Apr 25 '17 at 02:10
  • Do you redirect output when you're testing it, or is output to a terminal? – Barmar Apr 25 '17 at 02:10
  • @Barmar I can reproduce it quite easily. Does it have something to do with print/buffer> – Max Apr 25 '17 at 02:10
  • @Barmar I am printing on the terminal? – Max Apr 25 '17 at 02:11
  • If output isn't to a terminal, stdio is buffered, so you need to call `fflush(stdout)` after you print. But if you're printing to the terminal, it automatically flushes whenever you print `\n`. – Barmar Apr 25 '17 at 02:12

2 Answers2

1

The state of the stdio buffers is contained in the process's memory space. If the buffers are not empty at the time of a fork, the parent and child both have a copy of the information that says "Child process 3\n" is in the stdout buffer, waiting to be flushed.

And they will both eventually flush it.

You can avoid this by calling _exit instead of exit in the child process. exit is usually a bad idea when you're in a process that has not completed a successful exec since it was created.

0

The problem is your debugging output. It's undefined behavior to call printf in your async signal handler. There's only a short list of functions guaranteed to be safe during a signal handler.

If you remove it, you'll see the one outside of the signal handler never has unexpected behavior.

covener
  • 17,402
  • 2
  • 31
  • 45