the fork-then-exec pattern
How does your shell run a command like ls? It does not transform itself into ls (that would destroy the shell), and there is no single 'run this other program as a child' call on classic Unix. Instead it uses a two-step dance: first fork() to make a child copy of itself, then in that child call exec() to replace the copy with the real program. The parent (the shell) survives untouched and waits; the child becomes ls, runs, and finishes. This fork-then-exec pattern is how virtually every program on a Unix-like system launches another.
Walked through in plain steps: (1) The shell calls fork(). Now there are two shells, parent and child. (2) The child examines the return value, sees 0, and knows it is the child. It can do a little setup here that should affect only the new program - redirect its standard input/output, change directory, adjust the environment. (3) The child calls exec() on the target program, say execvp("ls", argv); the child's image is replaced and it is now running ls with whatever setup it arranged. (4) Meanwhile the parent saw a positive return from fork() (the child's PID) and calls wait() or waitpid() to pause until the child finishes and to collect its exit status. The elegance is the division of labor: fork gives you a fresh process to sacrifice, the brief window between fork and exec lets you customize it safely, and exec turns it into the program you actually wanted.
Why it matters: this is the canonical way to launch programs on Unix, and it explains design choices that otherwise look odd. Why are fork and exec separate calls instead of one combined 'spawn'? Precisely so you can insert that setup step in the child - redirecting a file descriptor, dropping privileges, setting environment variables - between making the new process and loading the new program. Higher-level helpers like the C library's system() and posix_spawn() wrap this pattern up, but underneath they are doing fork (or an equivalent) then exec then wait. Once you see this pattern, the whole choreography of shells, build tools, and servers spawning workers makes sense.
pid_t pid = fork(); if (pid == 0) { execvp("ls", (char*[]){"ls","-l",NULL}); _exit(127); } else { int status; waitpid(pid, &status, 0); } - parent forks, child execs into ls, parent waits and reaps. That is the whole life of running one command.
fork makes the child, exec turns it into the target program, wait reaps it - the launch trinity.
Keep the setup between fork and exec minimal and async-signal-safe; the child shares delicate state with the parent until exec replaces the image. This is also why posix_spawn() exists - a safer combined primitive for the common case.