Learn fork() command

 

What is fork()?

fork() is a POSIX system call that creates a new process by duplicating the calling (parent) process. The new process is the child. After fork() both processes continue execution from the point of the call, but each has its own memory space (copy-on-write), file descriptors, and execution context.

Key properties

  • Two processes after the call: parent and child.

  • Return value distinguishes them:

    • In the parent, fork() returns the child PID (>0).

    • In the child, fork() returns 0.

    • On error, fork() returns -1 in the parent and no child is created.

  • Copy-on-write: The address space is logically copied but physically shared until either process writes.

  • File descriptors are duplicated (share same open file description).

  • Child inherits environment, open files, signal dispositions (mostly).

  • Use wait() / waitpid() in parent to reap child; otherwise child can become a zombie until reaped.

  • If parent exits before child, child gets reparented to init (or systemd) — becomes an orphan.

Simple example — fork_demo.c

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main(void) { pid_t pid; printf("Before fork(): PID=%d, PPID=%d\n", getpid(), getppid()); pid = fork(); // create child if (pid < 0) { perror("fork failed"); exit(1); } else if (pid == 0) { // Child process printf("Child: PID=%d, PPID=%d, fork returned %d\n", getpid(), getppid(), pid); /* do some work to show different times */ for (int i = 0; i < 3; ++i) { printf("Child (%d): count %d\n", getpid(), i); sleep(1); } _exit(0); // use _exit to avoid flushing parent's stdio buffers twice } else { // Parent process printf("Parent: PID=%d, child PID=%d\n", getpid(), pid); /* wait for child to finish */ int status; waitpid(pid, &status, 0); printf("Parent: child %d exited with status %d\n", pid, WEXITSTATUS(status)); } return 0; }

Compile and run

gcc -Wall fork_demo.c -o fork_demo ./fork_demo

Typical output (annotated)

Before fork(): PID=4001, PPID=3995 Parent: PID=4001, child PID=4002 Child: PID=4002, PPID=4001, fork returned 0 Child (4002): count 0 Child (4002): count 1 Child (4002): count 2 Parent: child 4002 exited with status 0

Explanation:

  • The line before fork() shows only the original process.

  • After fork(), parent prints child PID, child prints zero return.

  • Parent waits until child exits (waitpid).

Inspecting process relationships

While program sleeps you can inspect:

ps -ef | grep fork_demo pstree -p <parent-PID> ls -d /proc/[0-9]* # shows process dirs

Important concepts and gotchas

Zombie processes

  • If parent does not wait() and child exits, child becomes a zombie (entry in process table with status Z) until parent reaps it.

  • Use wait()/waitpid() in parent or set SIGCHLD handler to SIG_IGN (with care) to avoid zombies.

Orphans

  • If parent exits while child runs, init/systemd adopts the child; child continues running.

exec after fork

  • Common pattern: fork() then in child call an exec*() (e.g., execvp()) to run a new program. This replaces child's image while keeping the same PID.

fork failure

  • fork() can fail (returns -1) when system resources are exhausted (e.g., RLIMIT_NPROC or kernel limits).

Race conditions

  • Because parent and child run concurrently, careful ordering with wait() or IPC is required when they share resources.

stdout buffering gotcha

  • If parent and child both inherit standard I/O buffers (line vs block buffering), printed lines may appear duplicated. Use _exit() in child after exec or flush buffers properly.

fork bomb (dangerous!)

  • A trivial fork misuse can create many processes quickly (e.g., :(){ :|:& };: in shell) — do not run on shared systems. Use process limits in labs (ulimit -u) to avoid system lockup.

Advanced notes (brief)

  • vfork() is a variant that suspends parent and intended to optimize immediate exec; use only with care.

  • clone() (Linux) allows more control (used to implement threads).

  • fork() duplicates file descriptor table but they share the same underlying file offset — careful with concurrent lseek().

Lab exercises

  1. Modify the example: child execvp() /bin/ls and parent prints waiting messages.

  2. Remove waitpid() and observe ps to find zombies; then fix by adding waitpid.

  3. Create a chain of forks (two successive fork() calls) and analyze how many processes are created (draw tree).

  4. Show reparenting: fork() a child that sleeps for 10s, parent exits immediately — inspect child's PPID.

Quick summary

  • fork() creates a new process; return value tells parent vs child.

  • Parent should manage child lifecycle (use waitpid).

  • Child can replace itself with exec*() or continue running different code.

  • Watch for zombies, orphans, buffering issues and resource limits.

Comments

Popular posts from this blog

Operating Systems OS Lab PCCSL407 Semester 4 KTU BTech CS 2024 Scheme - Dr Binu V P

Exploring the /proc file system

ps command