“Fork or die”

Glossary

fork()

(Defined in <unistd.h>)

fork() is a system call in Unix-like OSes. It creates a new process by duplicating the calling process. The new process (child) gets a copy of the parent’s memory, file descriptors, etc. Both parent and child continue execution from the point of fork(), but with different return values.

exec()

(Defined in <unistd.h>)

exec() replaces the current process image with a new one. It comes in several forms: execl(), execp(), execv(), etc. After a successful exec(), the original code is gone; only the new program runs. If it fails, it returns -1 and sets errno to indicate the error. The original process continues running with its current code. It’s often used after fork() in the child to run a different program.

wait()

(Defined in <sys/wait.h>)

wait() suspends the parent process until one of its child processes exits. It returns the child’s PID. It also stores the child’s exit status in an int passed by pointer. Using wait() prevents zombie processes.

The Unix Dilemma

In the turbulent world of operating systems, “Fork or die” isn’t just a saying, it’s a principle to live by.

Take Unix. While its competitors — Multics, CP/M, MS-DOS — perished nobly, the principle Unix cherished became its lifeline: it forked. It forked so well, we got Linux, BSD, macOS, and probably your toaster’s firmware. No fork? No future.

Now a chilling choice awaits every Unix process: fork() or die(). It’s Darwinism and perhaps also a spiritual journey. You think you’re running your script peacefully, and suddenly — bam! — you’re a parent. Welcome to the world of multiprocessing!

When you fork(), you duplicate yourself like a confused amoeba. One of you is the parent, the other is the child, and both are staring at the same code wondering who’s supposed to exec() and who’s just here for moral support.

But don’t get too comfortable. If your child exists and you don’t wait for it, it turns into a... a zombie. That’s right. Congratulations, you’re a necromancer. Why? Because you’ve created an undead process. It haunts the process table, consuming no resources except your soul and one precious PID.

Too many zombies and the kernel stops letting you fork(). Suddenly it’s not “fork() or die()” — it’s “fork() and everyone else dies.”

So, my fellow programmer, remember: fork() responsibly.

A Sample Program

            
// sample.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  printf("parent (pid: %d)\n", getpid());

  int rc = fork();

  if (rc < 0)
  {
    fprintf(stderr, "fork failed\n");
    exit(1);
  }
  else if (rc == 0)
  {
    printf("child (pid: %d)\n", getpid());

    char* myargs[3];
    myargs[0] = strdup("ls");
    myargs[1] = strdup("-l");
    myargs[2] = NULL;

    execvp(myargs[0], myargs);

    free(myargs[0]);
    free(myargs[1]);
  }
  else
    wait(NULL);
  
  return 0;
}
            
          

sample.c

The program creates a child with fork(). The child runs ls -l using execvp(), replacing its process image. The parent waits for the child to finish. If fork() fails, it prints an error and exits.