A short C program with two or three fork() calls can look as if it will create an unmanageable process forest. The trick is to stop reading it as ordinary sequential code. Track which existing processes reach each call, then duplicate only those processes.
Once that habit is fixed, most fork() questions are counting exercises with one classic off-by-one trap.
What fork() returns in the parent and child
A successful fork() creates one child process by duplicating the calling process. Both processes continue from the statement after the call, but they receive different return values:
In the child,
fork()returns0.In the parent, it returns the positive process ID of the child.
On failure, the parent receives
-1, and no child is created. Exam questions normally tell you to assume all calls succeed.
The parent and child have separate address spaces after the fork. Their variables begin with the same values, but a later assignment in one process does not change the other's copy.
If every existing process executes each of n unconditional sequential forks, the total number of processes is 2^n. The number of new child processes is 2^n - 1, because the original process is included in the total. Keep those two requested quantities separate.
The child also inherits copies of the parent's open file descriptors. The copies still refer to the same open files, so both processes share a single read and write offset on anything that was already open before the call. The wider process model and its state transitions are set out in Operating Systems for GATE.
Worked example 1: four sequential forks
The simplest form is a run of calls with nothing between them:
fork();
fork();
fork();
fork();Start with one process P0.
The first call is reached by one process. It creates one child, giving
2processes.Both processes reach the second call. Each creates a child, giving
2 x 2 = 4processes.All four reach the third call. Each creates a child, giving
4 x 2 = 8processes.All eight reach the fourth call. Each creates a child, giving
8 x 2 = 16processes.
Therefore, the final total is 16, while the number of newly created processes is 16 - 1 = 15. The shortcut gives the same result: 2^4 = 16 total.
Worked example 2: fork() inside a condition
Now trace a call whose return value controls a branch:
int x = 0;
if (fork() == 0) {
x = 1;
fork();
printf("%d ", x);
} else {
printf("%d ", x);
}Call the first fork F1. Its parent P0 receives a positive value, enters the else branch, and prints 0. Its child C1 receives 0, enters the if branch, and sets its private copy of x to 1.
Only C1 reaches the second fork F2. F2 creates child C2. Both C1 and C2 continue after F2 with their inherited value x = 1, so each prints 1.
The final count is therefore 3 processes: P0, C1, and C2. The output multiset is one 0 and two 1 values. Scheduling is nondeterministic, so the order of those three values is not fixed.

Do not apply 2^2 here. The second fork is reached by only one of the two processes created by F1. A process tree makes that restriction visible.
Worked example 3: fork() in a two-iteration loop
Consider a loop with the print after it:
for (int i = 0; i < 2; i++) {
fork();
}
printf("X\n");At i = 0, one process forks, so there are 2. Each process has its own loop variable and continues to i = 1. Both fork, so there are 4. All four leave the loop and execute printf once.
The program prints 4 lines. The formula confirms it: two unconditional iterations give 2^2 = 4 processes, and each performs one print after the loop.
If the print were inside the loop after the fork, the count would be different. There would be 2 executions in the first iteration and 4 in the second, for 2 + 4 = 6 printed lines. Count the processes that actually reach the print, not just the final leaves.
For more variations on this exact topic, use the process creation question set.
The printf buffer trap
Output already sitting in a user-space buffer is copied into the child at fork(). That can make the eventual output count exceed the number of times the source-level printf appears to execute.
printf("A");
fork();If A remains buffered when the fork occurs, both parent and child inherit a copy of that buffer and may later flush it. The visible result can contain A twice even though printf ran once before the fork.
With terminal output, a newline often flushes a line-buffered stream before the fork. Without a newline, the data is more likely to remain buffered. Redirection to a file switches the stream to full buffering, so a newline is a clue about what is still sitting in the buffer, not a guarantee that the buffer is empty.
The GATE-safe approach is to follow the buffering assumption stated in the question. If none is given and the print occurs after the fork, count one execution per process that reaches it. If the print occurs before the fork, check whether the question says the buffer was flushed. Do not silently mix process count with buffered-output count.
How GATE frames fork() questions
The common forms ask for the total processes, new children, times a line is printed, or a variable's value in a parent or child. Conditions, short-circuit operators, loops, wait(), and nested calls control which processes reach the next fork.
Short-circuit operators are the sharpest version of that restriction. In fork() && fork(); the second call is evaluated only where the first returned a non-zero value, which is the parent alone, so the program ends with 3 processes and not 4. In fork() || fork(); the second call is evaluated only where the first returned 0, which is the child alone, and the total is again 3. Settle the operator first, then ask which processes reach the call on its right.
Give every process one row in a small trace table: its name, the value fork() returned to it, its current variable values, and the next line it will execute. Add a row each time a call succeeds, and the totals fall out of the table instead of out of memory. The rest of the Operating Systems route for this exam sits in the GATE CS preparation category.
The short version
An unconditional fork doubles only the processes that execute it. Sequential unconditional calls give 2^n total processes and 2^n - 1 new ones. In conditions and loops, draw the tree and count the paths reaching each call or print. Treat inherited buffers as a separate issue.
Once the traces make sense, use the GATE Test Series to practise mixed counting questions under exam conditions. Always write whether your number means total processes, children created, or output lines.




