How does fork() work, and what are its return values?
fork() creates a new process by duplicating the calling process. The new process (child) is an almost exact copy of the parent.
Prototype:
#include <unistd.h>
pid_t fork(void);
Return values:
- In the parent: returns the child's PID (positive integer)
- In the child: returns 0
- On failure: returns -1 (no child created), sets errno
What the child inherits (copies of):
- Data segment, heap, stack (copy-on-write)
- Open file descriptors (shared open file table entries)
- Signal dispositions, environment, CWD, umask
- Real/effective UID and GID
What differs:
- PID (child gets a new PID)
- PPID (child's parent PID = original process PID)
- Pending signals (cleared in child)
- File locks (NOT inherited)
- Return value of fork()
After fork(), both parent and child continue executing from the instruction after the fork() call. The execution order is indeterminate.
1. pid = fork()
2. If pid == -1: error, no child created
3. If pid == 0: this is the child process
4. If pid > 0: this is the parent, pid = child's PID
5. Both processes continue from the line after fork()
6. Child is a copy-on-write duplicate of parent
7. Open file descriptors are shared (same open file table entries)
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void) {
int x = 42;
printf("Before fork: pid=%d, x=%d\n", getpid(), x);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
/* Child process */
x = 100; /* modifies child's copy only (COW) */
printf("Child: pid=%d, ppid=%d, x=%d\n", getpid(), getppid(), x);
_exit(0);
} else {
/* Parent process */
printf("Parent: pid=%d, child=%d, x=%d\n", getpid(), pid, x);
wait(NULL); /* wait for child */
printf("Parent: x still %d (unaffected by child)\n", x);
}
return 0;
}
- •fork() returns child PID to parent, 0 to child, -1 on error
- •Child is a copy-on-write duplicate of the parent
- •Open file descriptors are shared (same open file table entry)
- •Execution order after fork is indeterminate
- •Child gets new PID but inherits parent's data, heap, stack
- •stdio buffers are duplicated -- flush before fork to avoid double output
- •Shared file descriptors mean shared file offsets -- writes can interleave
- •fork() can fail with EAGAIN if process limit (RLIMIT_NPROC) is reached
- •The child should call _exit(), not exit(), to avoid flushing parent's stdio buffers