Fork system call question: How many times will star get printed?

How do you analyze this.

Based on the man pages:

The child process shall have a unique process ID.

Upon successful completion, fork() shall return 0 to the child process and shall return the process ID of the child process to the parent process. Both processes shall continue to execute from the fork() function. Otherwise, -1 shall be returned to the parent process, no child process shall be created, and errno shall be set to indicate the error.

In the above mentioned question:

fork()||fork()

What will it return? I can guess it returns true simply because it is an OR condition.

Then the inside code will get executed.

fork() function call once executed, it will create a parent and child processes.

Parent prints star.

Child prints star.

Finally, the outside of if statement star comes.

Here is the tough decision.

Will earlier child process still be active outside the if condition?

Nevertheless, a star should be printed here as well.

So I guess 3 stars are printed. Please help me analyze. I need to do this without compiler and IDE.

The issue here is that fork () returns integer values, but || works on boolean values. They are incompatible.

C does not have a proper Boolean type. Bool is represented by an integer which is zero for false and non-zero for true: it is not a single bit. The bool type is actually a char which is 0x00 for false, and anything from 0x01 to 0xFF for true.

fork() can return one of three values. It is also a rather weird function. It is called once, but if it works, it returns into the same code twice, once in the original process (which is now the parent), and again simultaneously into the child process, which has a complete copy of the parent code and data (description simplified somewhat).

The child (if is was even created) gets a zero value returned, which is therefore always false. If the child exists, plainly the fork() must have succeeded.

The parent gets either -1 if fork() fails, or the PID of the child, which is a positive integer. Both of those are non-zero, and therefore the value returned to the parent always evaluates as true, even if it failed.

The other issue is that the && and || operators short-circuit: that is, if the first condition decides the outcome unambiguously, the second condition is not even evaluated.

I will attempt to work through this, but your first guess is wrong because the OR condition is evaluating things outside of its scope, and possibly only evaluating one of the results anyway.

I get more than twice as many stars as you expect, and a surprising number of processes too.

Do you need to know what it does (numerically), or exactly why it does it, which may take me a while: I can’t even figure out the notation I need to understand what is going on.

I have at least printed out the PID along with each Star.

Running the code (with some debug added) shows me five processes, and nine stars, and I have a rational explanation of how that happens, which will follow shortly.

Each fork and printf is labelled for reference, and the Stars identify the process tree. I added a sleep so that none of the processes exits prematurely, because their children would all get reparented to PID 1.

I assume fork() never fails, so every fork returns twice, with two different values: the parent gets the child's PID, and the child gets a zero. I label the processes A to E in the narrative, but their actual pids appear in the debug. Process A is started by the shell, the other four processes are each started by a fork().

1:fork returns B in the parent A, which is (boolean) true. That satisfies the OR condition, so 2:fork is skipped and process A enters the inner clause.

1:fork returns zero in the child B, which is false. So the second part of the || needs to be evaluated.

2:fork returns C to child B (which is now also a parent), so the OR condition is true and B enters the inner clause.

2:fork returns zero to child C, so the OR condition is false, and process C skip the inner clause and goes direct to 5:.

3:fork is entered by two processes (directly by A, indirectly by B) and creates further processes D and E.

4:printf shows four processes from the inner clause: A, B, D, E.

5:printf shows all five processes, because C skipped the inner clause.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main (int argc, char *argv[]) 

{
char *Star = "[*]";

    if (/* 1: */ fork() || /* 2: */ fork()) {
        /* 3: */ fork();
        printf ("4: Pid %d Parent %d %s\n", getpid (), getppid (), Star);
    }
    printf ("5: Pid %d Parent %d %s\n", getpid (), getppid (), Star);
    sleep (3);  // Avoid children being reparented to Init.
    return (0);
}

paul: ~/SandBox/Fraction $ ./Fork
4: Pid 15120 Parent 11318 [*]
5: Pid 15120 Parent 11318 [*]
4: Pid 15122 Parent 15120 [*]
5: Pid 15122 Parent 15120 [*]
4: Pid 15121 Parent 15120 [*]
5: Pid 15121 Parent 15120 [*]
4: Pid 15124 Parent 15121 [*]
5: Pid 15124 Parent 15121 [*]
5: Pid 15123 Parent 15121 [*]
paul: ~/SandBox/Fraction $ 


FYI Only:

I loaded this problem to ChatGPT 5o a couple of day ago and the LLM also got:

9

Neo, Thanks for the confirmation. I added an analysis in post #4, but I was not happy with this: it concentrated on what the code was doing at each point. I think it would have been better to have followed each process independently.

I started that analysis from where each new process was created, and then a light dawned: each child has already run the whole of the code since entering main(), and contains all the data and resources from that code. None of that is lost during the fork: all that happens is that the child process from the fork is assigned a new PID, plus a new life of its own as a child, not as an orphan.

Most of my programs used the fork→execve model, and a few ran a separate set of functions in the same program, often with a pipe between parent and child. I never considered how having the parent and child continue to run the same code would work out.