1

I recently ran into this piece of code, and don't fully understand it.

  1. What circumstances would cause pid == 0?
  2. Why does wait(NULL) cause the program to go into the if(pid == 0)

Basically I don't fully understand the output below. Any help would be appreciated. Thank you.

Code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // standard POSIX header file
#include <sys/wait.h> // POSIX header file for 'wait' function
int main(void)
{
    int i = -1;
    int pid;
    pid = getpid();
    fprintf(stdout, "parent pid = %d\n", pid);
    pid = fork();

    if (pid == 0)
    {
        for (i = 0; i < 10; ++i)
        {
            fprintf(stdout, "child process: %d\n", i);
            sleep(1);
        }
        exit(0);
    }
    else
    {
        fprintf(stdout, "child pid = %d\n", pid);
        fprintf(stdout, "waiting for child\n");
        wait(NULL);
        fprintf(stdout, "child terminated\n");
    }
    fprintf(stdout, "parent terminating\n");
    return 0;
}

Output:

parent pid = 2896
child pid = 5840
waiting for child
child process: 0
child process: 1
child process: 2
child process: 3
child process: 4
child process: 5
child process: 6
child process: 7
child process: 8
child process: 9
child terminated
parent terminating
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
user3702643
  • 1,465
  • 5
  • 21
  • 48
  • The pid of the child process is received by the parent after `fork()` and both child and parent begin concurrent execution. Now, the parent's variable holding the pid has the child's pid, while the same variable in the child holds 0. – Susmit Agrawal Feb 24 '19 at 06:07
  • 2
    Remember that a successful `fork()` returns twice, once in each of two separate processes. In the child (new) process, the return value is always `0`. In the parent (old) process, the return value is the PID of the new (child) process. If the `fork()` fails, then the parent process gets a return value of `-1`, and there is no child process to worry about. – Jonathan Leffler Feb 24 '19 at 06:24
  • [`fork`](https://en.wikipedia.org/wiki/Fork_(system_call)) is a *difficult* function to understand (and even to explain). You need to spend several hours reading a few book chapters about it. Both [ALP](http://www.makelinux.net/alp/) and [OSTEP](http://pages.cs.wisc.edu/~remzi/OSTEP/) are books that you should read, and they need dozens of pages to explain `fork` – Basile Starynkevitch Feb 24 '19 at 07:00
  • Your code should call [fflush(3)](http://man7.org/linux/man-pages/man3/fflush.3.html), as `fflush(NULL);` *before* the `fork` – Basile Starynkevitch Feb 24 '19 at 07:03
  • `wait(NULL)`, forced parent to wait for child process until child finishes it work. https://stackoverflow.com/a/45762189/7508077 – EsmaeelE Feb 24 '19 at 07:27
  • "*What circumstances would cause pid == 0*" `fork()`'s docs are here: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html – alk Feb 24 '19 at 08:41
  • "*Why does wait(NULL) cause the program to go into the if(pid == 0)*" it doesn't. – alk Feb 24 '19 at 08:42
  • SciFi (rephrasing @JonathanLeffler): you enter alone in a cloning machine (call fork) and two persons get out (return from fork)... One is you, the other the clone. But clones are not really perfect (has you may know) : you get the ID of your clone (returned value pid of the clone), the clone knows he's the clone (return value 0). Now everyone is free to live his life with this. – Jean-Baptiste Yunès Feb 25 '19 at 13:04

2 Answers2

3

As mentioned in the comments by Susmit Agrawal and Jonathan Leffler, to briefly answer your first question:

The 0 is the returned value inside the child process after the fork() call succesfully returned. The execution of the child process starts in the if (pid == 0) block. It iterates over the for loop, prints and sleeps in each iteration, then in the end exit(0) is called, and the child process terminates.

The execution of the parent process after the fork() call continues in the else block. The PID returned in the parent process is the PID of the child.

To answer your second question briefly:

wait(NULL) is used to wait for changes of state of the child process. In this specific case the state change is the termination of the child. The parameter NULL simply means no status information will be stored.

For more detailed information please read the linked manual pages.

alk
  • 69,737
  • 10
  • 105
  • 255
  • For completeness, as this is taggd POSIX, please find the POSIX docs to `wait()` here: http://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html – alk Feb 24 '19 at 08:47
1
  1. The fork(2) system call creates a second, child process and as a consequence, the call is called once in the parent, but returns in both, the parent and the child.

    Due to this, the code following the call needs some indication to know if we are executing code in the parent or the child, because this allows parent and child to divert from the common code from this point on.

    The definition of fork(2) and it is stated in the manual page is:

    • to return -1 and set errno to a value indicating why the fork(2) call failed.
    • to return 0 for the child subprocess.
    • to return the pid_t child process id to the parent process (a positive number) so it can know the pid of the new subprocess just started.
  2. the wait(NULL); is not in the if (pid == 0), but in the else part, so it is indeed in the if (pid != 0) or in the parent process. The parent process must know if the child has finished and how, and must synchronise with it, for several reasons:

    • the child can exit(2) before the parent, but depending on the child's amount of work, it can do after the parent finishes. In case the parent ends before the child, you'll get the shell prompt again, and your screen will be blurred with the output of the child, right after the prompt has been issued. This is not what you normally desire, so a wait(2) call in the parent is just polite, so the shell only prompts you after everything has finished.

      A good test is to comment the wait(2) call in your example and see what happens.

    • the wait(2) system call is a means of knowing how your child ended it's run (mostly if you exec(2) some other program in the child's process) You can learn if your child died because it exit(2)ed (and receive its exit code), because it was interrupted (and what the signal it received was) or if it has been stopped by the user/system for some reason.

A reading of fork(2), exec(2), exit(2) and wait(2) system calls (all as a group of related calls) will enlighten all the process relationships and how all these system calls collaborate to manage the unix process system.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31