Process states, fork and exec
The life cycle of a process: the five-state model and its legal transitions, suspended states, the queues and the three schedulers, and how processes are created and ended with fork, exec, wait and exit, including zombies, orphans and counting the processes a fork program creates.
Why it exists
A process is not always doing the same thing: it may be running on the CPU, waiting for the CPU, or waiting for a disk. The operating system tracks this with a state and moves the process between queues as events happen. It also needs a way to create processes: on Unix-like systems fork makes a copy of the caller and exec replaces the copy's program. GATE asks which transitions are possible, what causes each, which scheduler does what, and above all how many processes (or how many lines of output) a program with several fork() calls produces.
Prerequisites
You need the process and its PCB from the process model.
Precise definitions
The five-state model.
| State | Meaning |
|---|---|
| New | being created (PCB and memory being set up) |
| Ready | in memory, able to run, waiting for the CPU |
| Running | executing on a CPU (at most one per CPU) |
| Waiting (blocked) | cannot run until some event, such as I/O completion, occurs |
| Terminated (exit) | finished; resources released, PCB kept until the parent collects the exit status |
Transitions and their causes.
| Transition | Cause |
|---|---|
| New to Ready | the OS admits the process (admission) |
| Ready to Running | the scheduler dispatches it |
| Running to Ready | preemption: the time slice expired (timer interrupt) or a higher-priority process became ready; or the process yields |
| Running to Waiting | the process asks for I/O or waits for an event (a system call that blocks) |
| Waiting to Ready | the event occurs (an I/O-completion interrupt) |
| Running to Terminated | the process exits or is killed |
There is no Ready to Waiting transition (a process that is not running cannot issue a request) and no Waiting to Running (a process whose event has occurred must queue for the CPU again). With a non-preemptive scheduler Running to Ready does not happen except by a voluntary yield.
Suspended states (swapping). If memory is short, the medium-term scheduler can swap out a process to disk. The seven-state model adds Ready-suspended and Blocked-suspended (also "swapped out" states): a blocked-suspended process becomes ready-suspended when its event occurs, and the process is brought back in by swapping in.
Queues. The job queue (all processes of the system), the ready queue (processes in memory ready to run, usually a linked list of PCBs) and device queues (processes waiting for a particular device). A process moves between them as its state changes.
The three schedulers.
- Long-term (job) scheduler: chooses which processes to bring into memory; it controls the degree of multiprogramming and runs rarely.
- Short-term (CPU) scheduler: chooses which ready process runs next; runs very often (every few milliseconds), so it must be fast.
- Medium-term scheduler: swaps processes out and back in to reduce the degree of multiprogramming.
Process creation (Unix).
fork()creates a child, a copy of the parent: same code, same next instruction, copies of the data, stack and heap (in practice copy-on-write), the same open files. It returns the child's pid in the parent, in the child, and on error.exec...()replaces the calling process's program by a new one: same pid and PCB, but new code, data, heap and stack. It does not return unless it fails. The usual use isfork()followed byexec()in the child.wait()makes the parent block until a child terminates, then returns the child's pid and exit status.exit()terminates the calling process.- The number of processes created by a program is found by following each
fork(): every executed call doubles the number of processes that execute the code after it.
Zombie and orphan. A zombie is a process that has terminated but whose parent has not yet called wait; its PCB stays so that the exit status can be collected. An orphan is a process whose parent has terminated first; it is adopted by the init process, which collects it when it ends.
Termination. A process ends when it calls exit, returns from main, is killed by a signal or by its parent, or causes a fatal error. When a process ends, its resources (memory, open files) are released, and its parent is notified.
How it works
The first animation walks one process through the five states. Notice which transitions exist and which do not, and that a process whose I/O completes goes to Ready, not straight to Running.
The five-state process model: one process's life: start
Simulated, not typedMoore machine: the output is written inside the state (state/output); edges are labelled with the input.
Run so far
Step 1
The five-state process model. A process is NEW while it is being created, READY when it could run but is waiting for the CPU, RUNNING while it has the CPU (only one per CPU), WAITING (blocked) until an I/O or other event completes, and TERMINATED when it has finished. Each arrow is an event. Notice what is missing: there is no arrow from READY to WAITING (a process that is not running cannot ask for I/O) and none from WAITING to RUNNING.
The next animations interpret small programs. Each shows the C fragment every process runs (with the line each process will execute next), the process table drawn as a tree, and the output so far. First the simplest repeated case, three fork() calls in a row: the number of processes doubles at each call.
Three forks in a row: start
Interpreted, not typed1int main(void) {2 fork();P13 fork();4 fork();5 puts("hi");6}
Step 0
One process, P1, starts running the fragment. fork() makes a new process that is a copy of the caller; the call returns the child's process id in the parent and 0 in the child, and afterwards both continue with the statement after the call. Step through to watch the process table grow. (The order in which the processes run is decided by the scheduler; here the lowest numbered runnable process always goes first, but any order gives the same COUNTS.)
Then the two short-circuit forms. In fork() && fork() only the parent evaluates the second fork (the child's value is 0, which already decides &&); in fork() || fork() only the child does.
fork() && fork(): start
Interpreted, not typed1int main(void) {2 fork() && fork();P13 puts("hi");4}
Step 0
One process, P1, starts running the fragment. fork() makes a new process that is a copy of the caller; the call returns the child's process id in the parent and 0 in the child, and afterwards both continue with the statement after the call. Step through to watch the process table grow. (The order in which the processes run is decided by the scheduler; here the lowest numbered runnable process always goes first, but any order gives the same COUNTS.)
fork() || fork(): start
Interpreted, not typed1int main(void) {2 fork() || fork();P13 puts("hi");4}
Step 0
One process, P1, starts running the fragment. fork() makes a new process that is a copy of the caller; the call returns the child's process id in the parent and 0 in the child, and afterwards both continue with the statement after the call. Step through to watch the process table grow. (The order in which the processes run is decided by the scheduler; here the lowest numbered runnable process always goes first, but any order gives the same COUNTS.)
When the child is distinguished by the return value, as in if (fork() == 0), the two sides run different code.
if (fork() == 0): only the child forks again: start
Interpreted, not typed1int main(void) {2 if (fork() == 0) {P13 fork();4 puts("child side");5 } else {6 puts("parent side");7 }8}
Step 0
One process, P1, starts running the fragment. fork() makes a new process that is a copy of the caller; the call returns the child's process id in the parent and 0 in the child, and afterwards both continue with the statement after the call. Step through to watch the process table grow. (The order in which the processes run is decided by the scheduler; here the lowest numbered runnable process always goes first, but any order gives the same COUNTS.)
And with wait() the parent blocks until its child has finished, which is the one case in which the output order is forced.
The parent waits for its children: start
Interpreted, not typed1int main(void) {2 if (fork() == 0) {P13 puts("child");4 } else {5 while (wait(NULL) > 0) { }6 puts("parent after wait");7 }8}
Step 0
One process, P1, starts running the fragment. fork() makes a new process that is a copy of the caller; the call returns the child's process id in the parent and 0 in the child, and afterwards both continue with the statement after the call. Step through to watch the process table grow. (The order in which the processes run is decided by the scheduler; here the lowest numbered runnable process always goes first, but any order gives the same COUNTS.)
Worked examples
EX 1Count the processes with the real fork
EasyA program runs each pattern in its own group of processes; every process, once its own children have finished, writes one byte into a pipe, so the number of bytes read equals the number of processes.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
/* Runs `pattern` in a fresh subtree of processes and counts how many processes executed the code after it:
every process, when it is done with its own children, writes one byte into a pipe; the number of bytes is the number of processes. */
static int count_processes(void (*pattern)(void)) {
int fd[2];
if (pipe(fd) != 0) return -1;
pid_t root = fork();
if (root < 0) return -1;
if (root == 0) {
pattern();
while (wait(NULL) > 0) { }
if (write(fd[1], "x", 1) != 1) _exit(1);
_exit(0);
}
close(fd[1]);
int status;
waitpid(root, &status, 0);
char c;
int n = 0;
while (read(fd[0], &c, 1) == 1) n++;
close(fd[0]);
return n;
}
static void three_forks(void) { fork(); fork(); fork(); }
static void and_fork(void) { fork() && fork(); }
static void or_fork(void) { fork() || fork(); }
static void child_forks(void) { if (fork() == 0) { fork(); } }
static void loop_forks(void) { for (int i = 0; i < 4; i++) fork(); }
static void two_blocks(void) { fork() && fork(); fork() && fork(); }
static void mixed(void) { (fork() && fork()) || fork(); }
int main(void) {
printf("fork(); fork(); fork(); -> %d processes\n", count_processes(three_forks));
printf("fork() && fork(); -> %d processes\n", count_processes(and_fork));
printf("fork() || fork(); -> %d processes\n", count_processes(or_fork));
printf("if (fork() == 0) { fork(); } -> %d processes\n", count_processes(child_forks));
printf("for (i = 0; i < 4; i++) fork(); -> %d processes\n", count_processes(loop_forks));
printf("fork() && fork(); (twice) -> %d processes\n", count_processes(two_blocks));
printf("(fork() && fork()) || fork(); -> %d processes\n", count_processes(mixed));
return 0;
}fork(); fork(); fork(); -> 8 processes
fork() && fork(); -> 3 processes
fork() || fork(); -> 3 processes
if (fork() == 0) { fork(); } -> 3 processes
for (i = 0; i < 4; i++) fork(); -> 16 processes
fork() && fork(); (twice) -> 9 processes
(fork() && fork()) || fork(); -> 5 processesReading it. unconditional forks in a row, or in a loop of iterations, give processes (the original and created). A single && or || between two forks gives 3. Two such blocks one after the other give : each of the 3 processes after the first block runs the second block. The last pattern, , gives 5: the parent makes two children (the left and the right of &&), and each of the two processes whose && came out false goes on to the || and makes one more.
EX 2Which transitions are possible?
EasyCheck each against the table of transitions. Ready to Running: yes (dispatch). Running to Waiting: yes (I/O request). Waiting to Running: no (the process must go to Ready first). Ready to Waiting: no. Waiting to Ready: yes (the event occurred). Running to Ready: yes with preemption (timer). New to Running: no (it must be admitted to Ready first). Terminated to anything: no. A question with a list of transitions asks you to pick the impossible ones; apply the table.
EX 3How many lines does it print?
Mediumfork(); fork(); printf("hi\n"); creates 4 processes, each of which prints, so 4 lines. fork(); printf("a\n"); fork(); printf("b\n");: after the first fork there are 2 processes, each prints a (2 lines of a); each then forks, giving 4 processes, each printing b (4 lines of b): 6 lines in all. The count of prints of each statement equals the number of processes that reach it, which is the number of processes alive at that point, found by doubling at each fork. (When output is redirected to a file or pipe, a program that does not flush its buffer before fork may print buffered text twice, as the child's copy of the buffer is also flushed; with a terminal and \n the buffer is flushed first, so the simple count above holds.)
EX 4Conditions on the return value
MediumIn if (fork() == 0) { fork(); } printf("x\n"); the original process forks once; the child (return value 0) executes the if body and forks again, so the processes are the parent, the child and a grandchild: 3 processes, 3 lines. In if (fork() != 0) fork(); the parent forks again instead, again 3 processes, but now the parent has two children and the child has none. The tree shape differs even though the count is the same, and the shape decides questions such as "how many children does the original process have".
EX 5Zombie, orphan or neither?
GATE levelA child calls exit() and the parent is still running but never calls wait: the child is a zombie (it stays in the process table, state Terminated, until the parent waits). A parent calls exit() while the child is still running: the child is an orphan, adopted by init; when it ends, init collects it, so it does not remain a zombie. A parent that terminates while a child is a zombie: init adopts and reaps the zombie. A zombie uses no CPU and no memory except its PCB entry; its danger is filling the process table.
Cheat box
Cheat box
- States: New, Ready, Running, Waiting, Terminated. Legal: New to Ready, Ready to Running, Running to Ready (preemption), Running to Waiting (I/O), Waiting to Ready (event), Running to Terminated.
- Never: Ready to Waiting, Waiting to Running, New to Running.
- Schedulers: long-term (admission, degree of multiprogramming), short-term (CPU, frequent), medium-term (swapping).
fork(): returns child's pid to the parent and 0 to the child; child is a copy.exec: replaces the program, same pid.wait: blocks for a child.exit: terminates.- sequential or looped forks: processes.
fork() && fork()andfork() || fork(): 3 processes. Repeated blocks multiply. - Zombie: terminated, not yet waited for. Orphan: parent gone, adopted by init.
Traps and GATE patterns
Connections
Builds on
Unlocks
Across subjects
No cross-subject links yet.
The PCB that each state refers to is described in the process model. Moving the CPU from the running process to a ready one is the context switch, and the short-term scheduler's choice is the subject of CPU scheduling. fork, exec, wait and exit are system calls. A thread is created far more cheaply than a forked process.
PYQs
6 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 5 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider a process P running on a CPU. Which one or more of the following events will always trigger a context switch by the OS that results in process P moving to a non-running state (e.g., ready, blocked)?
- (a)P makes a blocking system call to read a block of data from the disk
- (b)tries to access a page that is in the swap space, triggering a page fault
- (c)An interrupt is raised by the disk to deliver data requested by some other process
- (d)A timer interrupt is raised by the hardware OPERATING SYSTEMS
Which of the following process state transition(s) is/are NOT possible?
- (a)Ready to Waiting
- (b)Running to Ready
- (c)Running to Terminated
- (d)Waiting to Running
Consider the following statements about process state transitions for a system using preemptive scheduling. I. A running process can move to ready state. II. A ready process can move to running state. III. A blocked process can move to running state. IV. A blocked process can move to ready state. Which of the above statements are TRUE?
- (a)I, II, and III only
- (b)II and III only
- (c)I, II, and IV only
- (d)I, II, III and IV only
The maximum number of processes that can be in Ready state for a computer system with n CPUs is
- (a)n
- (b)n2
- (c)2N
- (d)Independent of n
In the following process state transition diagram for a uniprocessor system, assume that there are always some processes in the ready state : Now consider the following statements : I. If a process makes a transition D, it would result in another process making transition A immediately. II. A process P2 in a blocked state can make transition E while another process P1 is in running state. III. The OS uses preemptive scheduling. IV. The OS uses non-preemptive scheduling. Which of the above statements are TRUE ?
- (a)I and II
- (b)I and III
- (c)II and III
- (d)II and IV
Suppose in a multiprogramming environment, the
following C program segment is executed. A process
goes into I/O queue whenever an I/O related
operation is performed. Assume that there will always
be a context switch whenever a process requests for
an I/O, and also whenever the process returns from an
I/O. The number of times the process will enter the
ready queue during its lifetime (not counting the time
the process enters the ready queue when it is run
initially) is ______
Int main()
{
int x = 0, i = 0;
scanf("%d", &x);
for(i = 0; i < 20; i++)
{
x = x + 20;
printf("%d\n", x);
}
return 0;
}
OPERATING SYSTEMSSelf-check
Question 1
How many processes in all are there after fork(); fork(); fork(); fork();, and how many were created?
Show answerHide
in all; 15 created.
Question 2
A process is Running and issues a read on a disk file. Which state does it enter, and what moves it out of that state?
Show answerHide
Waiting (blocked). The disk's I/O-completion interrupt moves it to Ready.
Question 3
In if (fork() == 0) { if (fork() == 0) fork(); } how many processes in all?
Show answerHide
The original forks (child A). A forks (child B). B forks (child C). Four processes: the original, A, B, C. The original never executes the inner fork.
Question 4
Which scheduler decides the degree of multiprogramming?
Show answerHide
The long-term scheduler (the medium-term scheduler can also reduce it by swapping).
Revision card
One-screen revision
- Five states; legal arrows: admit, dispatch, timeout, I/O wait, I/O done, exit. Not Ready to Waiting, not Waiting to Running.
- Long-term, short-term, medium-term schedulers.
- fork: 0 in the child, child's pid in the parent; exec keeps the pid; wait blocks; zombie and orphan.
- forks: processes;
&&: parent only,||: child only, 3 processes each.
References
- ·Silberschatz, Galvin, Gagne, Operating System Concepts, Chapter 3 Processes (process state, scheduling queues, operations on processes)
- ·Tanenbaum and Bos, Modern Operating Systems, Chapter 2 Processes and Threads (process creation, termination, states)
- ·Stallings, Operating Systems: Internals and Design Principles, Chapter 3 Process Description and Control (process states, suspended processes, process creation)
Verification
The number of processes created by each fork pattern is produced by a real-fork C program on this page (npm run verify:c), by the interpreter in scripts/gen-os-proc-traces.py and by compiling the same programs and running them with the host's real fork in scripts/enum-checks/os_proc.py (the page programs and 20 random programs, comparing the process count and every printed line). The state-machine animation is checked by tests/os-proc-traces.test.ts against the legal transitions of the five-state model. The output ORDER of processes is decided by the scheduler and is not fixed; the page and the animations give only counts and the orders that are forced by wait. State names differ by book (waiting or blocked; terminated or exit; some books have no new state). Textbook chapter titles are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand. No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)