0

In the program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int pid;
    int status;

    pid = fork();

    if (pid < 0)
    {
        return -1;
    }
    else if (pid == 0)
    {
        printf("I am the child\n");
        exit(0);
    }
    else if (pid > 0)
    {
        pid = wait(&status);
        printf("I am the parent\n");
        printf("My pid is %i\nMy childs's pid is %i\nMy childs's exit status is     %i\n", getpid(), pid, status);

    }

}

This program returns:

I am the child
I am the parent
My pid is 16269
My childs's pid is -1
My childs's exit status is 32767

1) Why is the child's pid = -1 and not the actual child process number?

2) Why is the child's exit status != 0, as I intended?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
user3614293
  • 385
  • 5
  • 13

2 Answers2

3

Regarding the child process id, it's because you reassign the pid variable, and wait returns -1 on an error.

Regarding the exit status, it's invalid since wait failed. But otherwise it's only the eight lowest bits that contain the actual status. First you need to make sure that the wait call actually succeeded, then you need to check if the process exited normally with the WIFEXITED macro, and then get the actual exit status with the WEXITSTATUS macro.

I suggest you read a reference for wait and the <sys/wait.h> header file.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • But why did the wait fail? When I run the code, I don't get the error. – Jonathan Leffler Jan 31 '15 at 23:33
  • @JonathanLeffler That is a good question, and one only the OP can answer, by checking [`errno`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html) when the function fails (OP: Hint hint...) – Some programmer dude Jan 31 '15 at 23:34
  • I added sleep(10) to parent and now I'm getting I am the child I am the parent My pid is 16536 My childs's pid is 16539 My childs's exit status is 256. The exit status is still not 0. Is it normal? – user3614293 Jan 31 '15 at 23:37
  • @user3614293 As for the exit status, read my answer again. As for *why* `wait` fails, you have to check `errno`. – Some programmer dude Jan 31 '15 at 23:38
  • Note that it is the low-order 8 bits of the value passed to `exit()` that are returned by `wait()` et al, but traditionally (if you aren't using the W* macros from ``), those bits are placed in the high order bits of a 16-bit value of the status returned by `wait()`. That is, if the child exits with `exit(0x12)`, then the value returned by `wait()` will be `0x1200`. The low-order 8 bits encode signal number and core dump information. I think that's what you meant, but "it's only the eight lowest bits that contain the actual status" is ambiguous between `wait()` and `exit()`. – Jonathan Leffler Apr 08 '15 at 16:14
2

Diagnosis

I think you must be running the process with the SIGCHLD signal ignored.

When I run your code normally, I get output such as:

$ ./wp0
I am the child
I am the parent
My pid is 80161
My childs's pid is 80162
My childs's exit status is     0
$

But, if I run it with the SIGCHLD signal ignored, I get:

$ (trap '' CHLD; ./wp0)
I am the child
I am the parent
My pid is 80435
My childs's pid is -1
My childs's exit status is     0
$

You can check this, perhaps, by running 'trap' at the shell command line. For example, I get:

$ (trap '' CHLD; trap)
trap -- '' SIGCHLD
$

If using trap (with no arguments) doesn't produce an answer analogous to that, then you may need to write a program to interrogate the SIGCHLD status.

Incidentally, one of the first things I did (before going back to your original code), was change the parent's code to:

    int corpse = wait(&status);
    printf("I am the parent\n");
    printf("My pid is %i\nMy child's pid was %i\nMy child's exit status is %i\n", getpid(), pid, status);
    printf("My child's corpse was %i\n", corpse);

It captures the return value from wait() separately from the return value from fork(). Also, I normally print the exit status using 0x%.4X — in part because I'm anal-retentive about the format for hex values but mainly because the hex value is easier to understand. You can experiment with the child exiting with different values (perhaps interpreting a command-line argument), and even with it committing suicide with different signals. While the WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG etc macros are good, they also are also quite simple and you can see what they do by looking at the hex status.

Creating a test bed for exit statuses and signals

I extended your program to create a working test harness that allows you to experiment with exit values and signal handling like this:

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

int main(int argc, char **argv)
{
    int c_status = 0;
    int c_signal = 0;
    int opt;

    while ((opt = getopt(argc, argv, "s:e:")) != EOF)
    {
        switch (opt)
        {
        case 's':
            c_signal = atoi(optarg);
            break;
        case 'e':
            c_status = atoi(optarg);
            break;
        default:
            fprintf(stderr, "Usage: %s [-e status][-s signal]", argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    sigset_t oset;
    if (pthread_sigmask(SIG_BLOCK, 0, &oset) != 0)
    {
        fprintf(stderr, "%s: pthread_sigmask() failed (%d: %s)\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    if (sigismember(&oset, SIGCHLD))
        printf("SIGCHLD is listed in the signal mask\n");
    struct sigaction oact;
    if (sigaction(SIGCHLD, 0, &oact) != 0)
    {
        fprintf(stderr, "%s: pthread_sigmask() failed (%d: %s)\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    if (oact.sa_handler == SIG_DFL)
        printf("SIGCHLD handling is the default\n");
    else if (oact.sa_handler == SIG_IGN)
        printf("SIGCHLD handling is ignoring the signal\n");
    else
        printf("SIGCHLD has a signal handler installed (%p)\n", (void *)oact.sa_handler);
    if (oact.sa_flags & SA_NOCLDSTOP)
        printf("SA_NOCLDSTOP is set\n");
    if (oact.sa_flags & SA_NOCLDWAIT)
        printf("SA_NOCLDWAIT is set\n");

    int pid = fork();
    if (pid < 0)
    {
        return -1;
    }
    else if (pid == 0)
    {
        printf("I am the child - PID %d\n", (int)getpid());
        if (c_signal == 0)
        {
            printf("I am exiting with status %d\n", c_status);
            exit(c_status);
        }
        else
        {
            printf("I am committing suicide with signal %d\n", c_signal);
            raise(c_signal);
            printf("That didn't work - I'm still here. Oh well!\n");
            exit(EXIT_FAILURE);
        }
    }
    else if (pid > 0)
    {
        int status;
        int corpse = wait(&status);
        printf("I am the parent\n");
        printf("My pid is %i\n", getpid());
        printf("My child's pid was %i\n", pid);
        printf("My child's corpse was %i\n", corpse);
        printf("My child's exit status is 0x%.4X\n", status);
        if (WIFEXITED(status))
            printf("My child exited normally with status %d\n", WEXITSTATUS(status));
        if (WIFSIGNALED(status))
        {
            printf("My child died from signal %d\n", WTERMSIG(status));
#ifdef WCOREDUMP
            if (WCOREDUMP(status))
                printf("My child produced a core dump\n");
#endif
        }
    }
    return 0;
}

I called the program wp2. Running it a few times with different options gave output like this:

$ ulimit -c unlimited
$ ./wp2 -s 4
SIGCHLD handling is the default
I am the child - PID 81188
I am committing suicide with signal 4
I am the parent
My pid is 81187
My child's pid was 81188
My child's corpse was 81188
My child's exit status is 0x0004
My child died from signal 4
$ ./wp2 -s 6
SIGCHLD handling is the default
I am the child - PID 81224
I am committing suicide with signal 6
I am the parent
My pid is 81223
My child's pid was 81224
My child's corpse was 81224
My child's exit status is 0x0086
My child died from signal 6
My child produced a core dump
$ ulimit -c 0
$ ./wp2 -s 6
SIGCHLD handling is the default
I am the child - PID 81489
I am committing suicide with signal 6
I am the parent
My pid is 81488
My child's pid was 81489
My child's corpse was 81489
My child's exit status is 0x0006
My child died from signal 6
$ (trap '' CHLD; ./wp2 -s 6)
SIGCHLD handling is ignoring the signal
SA_NOCLDWAIT is set
I am the child - PID 82300
I am committing suicide with signal 6
I am the parent
My pid is 82299
My child's pid was 82300
My child's corpse was -1
My child's exit status is 0x0000
My child exited normally with status 0
$ ./wp2 -e 6
SIGCHLD handling is the default
I am the child - PID 82434
I am exiting with status 6
I am the parent
My pid is 82433
My child's pid was 82434
My child's corpse was 82434
My child's exit status is 0x0600
My child exited normally with status 6
$

The fact that it is easy to interpret the raw status is not an excuse to avoid using the macros provided by POSIX. Note that WCOREDUMP is not specified by POSIX, but is usually available.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278