Process Creation: fork, exec, wait
IntermediateUnix process creation uses fork() to duplicate the parent, exec() to replace the process image with a new program, and wait() for the parent to collect the child's exit status.
Overview
In Unix/Linux, every process (except init/systemd) is created by another process. fork() creates a child that is an almost-exact copy of the parent, using copy-on-write (COW) so memory pages are only physically copied when written. exec() replaces the calling process's code and data with a new program — the PID stays the same. wait() (or waitpid()) blocks the parent until the child exits, collecting its exit status and preventing zombie processes. A zombie is a process that has exited but whose PCB remains because the parent hasn't called wait(). An orphan is a process whose parent exited first — adopted by init. Java exposes process creation through ProcessBuilder and Process APIs.
fork, exec, wait — How the Shell Works
When you type a command in a shell, the shell calls fork() to clone itself, then the child calls exec() to replace itself with the command binary. The shell (parent) calls wait() to get the exit code. Copy-on-write means fork() is cheap — pages are shared until either process writes to them, only then is a physical copy made.
// Unix process creation (C pseudocode — conceptual)
pid_t pid = fork(); // duplicate current process
if (pid == 0) {
// Child process: replace image with "ls -la"
execl("/bin/ls", "ls", "-la", NULL);
// exec never returns on success
perror("exec failed");
exit(1);
} else if (pid > 0) {
// Parent process: wait for child to finish
int status;
waitpid(pid, &status, 0); // blocks until child exits
if (WIFEXITED(status)) {
printf("Child exited with: %d\n", WEXITSTATUS(status));
}
} else {
perror("fork failed");
}
// A zombie occurs if parent exits without calling wait()
// An orphan occurs if parent exits before child — init adopts itJava ProcessBuilder: fork+exec in the JVM
Java's ProcessBuilder wraps the OS fork+exec model. On Unix it internally calls posix_spawn (or fork/exec). You can redirect stdin/stdout/stderr, set environment variables, and use waitFor() to block until the child exits — equivalent to wait() in C.
// Java ProcessBuilder — spawn a child process
ProcessBuilder pb = new ProcessBuilder("ls", "-la", "/tmp");
pb.redirectErrorStream(true); // merge stderr into stdout
pb.directory(new File(System.getProperty("user.home")));
Process child = pb.start();
// Read child's stdout
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(child.getInputStream()))) {
reader.lines().forEach(System.out::println);
}
int exitCode = child.waitFor(); // blocks — equivalent to wait()
System.out.println("Exit code: " + exitCode);
// Accessing child's PID (Java 9+)
System.out.println("Child PID: " + child.pid());
// Detecting zombie-equivalent: child finished, exitCode collected
// In Java, the JVM handles zombie cleanup automatically via waitFor()Zombie and Orphan Processes
A zombie process has finished executing but its PCB remains in the process table because the parent hasn't called wait(). It consumes no CPU or memory except a PCB slot. Too many zombies can exhaust PID space. An orphan process is a running process whose parent has exited — in Unix, init (PID 1) automatically adopts orphans and calls wait() for them.
// Java: detecting and handling child process lifecycle
ProcessBuilder pb = new ProcessBuilder("sleep", "5");
Process child = pb.start();
// If parent exits here without calling waitFor(), child becomes orphan
// Java's Process is NOT automatically waited — always call waitFor() or destroy()
// Timeout-based wait (Java 9+)
boolean finished = child.waitFor(10, TimeUnit.SECONDS);
if (!finished) {
child.destroyForcibly(); // sends SIGKILL
System.out.println("Child killed after timeout");
} else {
System.out.println("Child exited: " + child.exitValue());
}
// Register shutdown hook to clean up child processes
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (child.isAlive()) child.destroyForcibly();
}));Key Points to Remember
- 1fork() creates a child process as a copy of the parent using copy-on-write pages.
- 2exec() replaces the process image with a new program; the PID is preserved.
- 3wait()/waitpid() collects the child's exit status and removes its PCB from the process table.
- 4A zombie process has exited but its PCB remains because the parent hasn't called wait().
- 5An orphan process's parent has exited; init (PID 1) adopts it and calls wait().
- 6Java ProcessBuilder internally uses posix_spawn/fork+exec; waitFor() is equivalent to wait().
Interview Questions
Sign in to ask AriaWhat is copy-on-write and how does it make fork() efficient?
What is a zombie process and how do you prevent it?
What is the difference between a zombie and an orphan process?
How does Java's ProcessBuilder map to Unix fork/exec/wait?
Ask Aria about Process Creation: fork, exec, wait
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.