G27
Self-checkedTier 1 13 min reados.process-states-creation

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.

StateMeaning
Newbeing created (PCB and memory being set up)
Readyin memory, able to run, waiting for the CPU
Runningexecuting 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.

TransitionCause
New to Readythe OS admits the process (admission)
Ready to Runningthe scheduler dispatches it
Running to Readypreemption: the time slice expired (timer interrupt) or a higher-priority process became ready; or the process yields
Running to Waitingthe process asks for I/O or waits for an event (a system call that blocks)
Waiting to Readythe event occurs (an I/O-completion interrupt)
Running to Terminatedthe 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, 00 in the child, and −1-1 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 is fork() followed by exec() 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 typed
admitdispatchtimeoutI/O requestI/O doneexitNew/being createdReady/waiting for the CPURunning/using the CPUWaiting/waiting for I/O or an eventTerminated/finished

Moore machine: the output is written inside the state (state/output); edges are labelled with the input.

Run so far

input (none)
output (none)

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.

1 / 9
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

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 typed
1int main(void) {
2 fork();P1
3 fork();
4 fork();
5 puts("hi");
6}
processes
1
lines printed
0
P1no parentready

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.)

0 / 16
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

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 typed
1int main(void) {
2 fork() && fork();P1
3 puts("hi");
4}
processes
1
lines printed
0
P1no parentready

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.)

0 / 7
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

fork() || fork(): start

Interpreted, not typed
1int main(void) {
2 fork() || fork();P1
3 puts("hi");
4}
processes
1
lines printed
0
P1no parentready

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.)

0 / 7
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

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 typed
1int main(void) {
2 if (fork() == 0) {P1
3 fork();
4 puts("child side");
5 } else {
6 puts("parent side");
7 }
8}
processes
1
lines printed
0
P1no parentready

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.)

0 / 8
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

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 typed
1int main(void) {
2 if (fork() == 0) {P1
3 puts("child");
4 } else {
5 while (wait(NULL) > 0) { }
6 puts("parent after wait");
7 }
8}
processes
1
lines printed
0
P1no parentready

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.)

0 / 7
computed by script: an interpreter or machine simulation, one action per step. python3 scripts/gen-os-proc-traces.py

Worked examples

EX 1Count the processes with the real fork

Easy

A 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 processes

Reading it. nn unconditional forks in a row, or in a loop of nn iterations, give 2n2^n processes (the original and 2n−12^n - 1 created). A single && or || between two forks gives 3. Two such blocks one after the other give 3×3=93 \times 3 = 9: each of the 3 processes after the first block runs the second block. The last pattern, (a  &&  b)  ∣∣  c(a \;\&\&\; b) \;||\; c, 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?

Easy

Check 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?

Medium

fork(); 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

Medium

In 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 level

A 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.
  • nn sequential or looped forks: 2n2^n processes. fork() && fork() and fork() || fork(): 3 processes. Repeated blocks multiply.
  • Zombie: terminated, not yet waited for. Orphan: parent gone, adopted by init.

Traps and GATE patterns

Connections

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

GATE 2024 · MSQQ5 · page 295 of the book
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
GATE 2024 · MSQQ8 · page 296 of the book
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
GATE 2020 · MCQQ21 · page 298 of the book
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
GATE 2015 · MCQQ30 · page 300 of the book
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
GATE 2009 · MCQ · 2 marksQ45 · page 303 of the book
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
GATE 2025 · NATQ4 · page 357 of the book
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 SYSTEMS

Self-check

Question 1

How many processes in all are there after fork(); fork(); fork(); fork();, and how many were created?

Show answer

24=162^4 = 16 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 answer

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 answer

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 answer

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.
  • nn forks: 2n2^n 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