Lesson 3 | Forking |
Objective | Describe the mechanism by which processes come into being. |
Unix Fork Call
In UNIX-based systems, new processes come into existence through a well-defined mechanism involving two primary system calls:
- `fork()` and
- `exec()`.
Here’s a detailed breakdown of this mechanism:
①Forking (`fork()` System Call):
- The
fork()
system call creates a new process called a child process, duplicating the calling (parent) process.
- Both processes (parent and child) continue executing immediately after the
fork()
call.
- The child is a near-exact copy of the parent process: it has a copy of the parent’s memory space, file descriptors, signal handlers, and execution state.
Example (pseudo-code):
pid_t pid = fork();
if (pid < 0) {
// Fork failed
} else if (pid == 0) {
// Child process executes here
} else {
// Parent process executes here, pid contains child's PID
}
After `fork()`:
- Child process returns
0
.
- Parent process receives the child’s Process ID (PID).
②Executing a Program (`exec()` System Call):
- The child process typically executes a different program by calling one of the
exec()
family system calls (execl
, execvp
, execve
, etc.).
exec()
completely replaces the child’s process image (code, data, stack) with a new program loaded from disk into memory.
Example (pseudo-code):
if (pid == 0) {
execl("/bin/ls", "ls", "-l", NULL);
// If exec() returns, an error has occurred
}
Combined Mechanism (`fork()` + `exec()`):
Most UNIX processes follow this standard two-step process:
fork()
– Create a new, identical copy of the calling process.
exec()
– Load and execute a new executable into this newly created process.
This approach is fundamental because:
- It provides clear separation between process creation (
fork
) and program execution (exec
).
- Enables setting up environment variables, file descriptors, and other resources for the child before the actual execution.
- Allows powerful and flexible management of processes, pipelines, and job control.
③Parent–Child Relationship:
-
Every process (except the initial system process, typically
init
or systemd with PID 1) is created by a parent.
-
Processes form a tree structure:
- The initial parent process (
init
/systemd
) creates system processes.
- User shells (
bash
, zsh
) spawn user-level applications as their children.
-
The parent can:
- Wait (
wait()
call) for the child process to terminate.
- Handle child termination status (exit codes or signals).
④Process Lifecycle Overview:
Parent Process
│
│ fork()
▼
Child Process (exact copy)
│
│ exec()
▼
Child executes a new program
---
Key System Calls Recap:
Call |
Functionality |
fork() |
Creates a duplicate of the calling process (child). |
exec() |
Replaces the current process image with a new executable. |
wait() |
Parent waits for child process completion. |
_exit() |
Process termination call to end a process cleanly. |
Summary:
In UNIX, a new process is created by first duplicating an existing one (`fork()`) and then replacing its code and memory with a new program (`exec()`). This two-step mechanism is the fundamental design behind all UNIX process management and creation.
Birth, life, and death of Process
All processes on a UNIX system come into being in the same way:
They are cloned from an already running process.
For example, suppose that a user types the command.
grep yes *.c
to his or her login shell. At this point, the shell needs to start a new grep process, wait for it to finish, then generate a new
output prompt and wait for the next command. The mechanism by which this occurs is called
forking.
fork(), exec(), wait()
Many aspects of the behavior of UNIX processes are consequences of how they are born. Behind the scenes, the procedure looks like this:
- The shell process executes a system call called
fork()
.
This system call asks the operating system to clone whatever process calls it.
- As a result of the
fork()
call, the system replaces the shell process with two new processes. These processes are
identical in every respect, except that one is labeled "parent" and the other is labeled "child." The parent process, which is identical to the original shell, then begins to wait for the child process to terminate.
- The child process, which is a copy of the original shell, now makes a second system call, called
exec()
. While
fork()
clones a process, exec()
starts a new process that replaces whatever process called it. In this instance, the child shell exec is the new grep process, and the child shell disappears, replaced by grep.
- The grep process terminates, and the parent shell, which was waiting for this event, wakes up and generates a new login prompt.
Process ID and parent process ID
Every process on the system is numbered, starting from 1. This number is called the Process ID (PID). The ancestor of all processes on
the system is the process init, which is started at
boot[1] time and is automatically given the PID of 1. All other processes
on the system are derived from init by the fork and exec procedures, either directly or indirectly. Because every process results from
a
fork()
, every process has a parent process, the process that called the
fork()
that created it. Because all
processes derive ultimately from init with PID 1, tracing the chain of ancestry back from any running process must ultimately lead to
init.
Killing a process
If you know a process’s PID, then you can terminate that process by using the
kill
command.
For example, to stop a process with the PID 34, you would type.
kill 34
Safest ways to kill Multiple Processes in Unix
Safely killing multiple processes in Unix involves ensuring that you terminate only the intended processes without disrupting essential services or causing data corruption. Here are some recommended, safe approaches:
-
Using
pkill
(Safest and Recommended)
Syntax:
pkill -signal -u username -f "process_pattern"
Example (terminate gracefully):
pkill -TERM -u oracle -f "sqlplus"
Example (forceful kill, after graceful attempt fails):
pkill -KILL -u oracle -f "sqlplus"
-TERM
(default) politely requests termination, allowing processes to perform cleanup.
-KILL
immediately terminates processes; use only when -TERM
fails.
-
Using
killall
(Second safest)
killall
works similarly to pkill
but requires exact process names:
Example (graceful):
killall -TERM firefox
Example (forceful):
killall -KILL firefox
- Caution: Be careful with common names that might match multiple unintended processes.
-
Using Process IDs (PID) explicitly (Very safe and precise)
You can carefully pick processes and kill them explicitly:
ps aux | grep [p]rocess_name
kill -TERM pid1 pid2 pid3
Example:
kill -TERM 1234 5678 9012
To forcefully kill (only if gentle attempts fail):
kill -KILL 1234 5678 9012
-
Using
xargs
and ps
(safe if used carefully)
To kill processes matching a pattern:
ps aux | grep '[p]rocess_pattern' | awk '{print $2}' | xargs -r kill -TERM
Example:
ps aux | grep '[j]ava -jar app.jar' | awk '{print $2}' | xargs -r kill -TERM
- This approach is more explicit and provides clarity on which PIDs are targeted.
General Safety Guidelines:
- Always attempt a graceful termination (
SIGTERM
) first, giving the process a chance to clean up.
- Use the forceful kill (
SIGKILL
) only when necessary.
- Double-check which processes will be terminated using
ps
, pgrep
, or pidof
.
- Avoid running as root unless necessary—using specific user (
-u username
) reduces risk.
- Always confirm your commands (for example, by first listing processes with
pgrep -a
) before execution.
Following these steps ensures safe termination of multiple Unix processes with minimal risk to your system stability and data integrity.
Forking Process - Quiz
[1]boot: To boot a system means to start it up. Many systems are configured to boot automatically after the power comes on or after a system crash.
