How child processes are managed in Linux
Published on 2026-09-12 by frfrand
Qué hacen fork y exec, por qué existen los procesos zombis y qué significan realmente, quién adopta a los huérfanos, y por qué tu contenedor ignora la señal de apagado.

In Linux there is no call to "create a process from scratch". There is one to copy itself, and another to replace the program you are running. Everything else comes from combining those two.
fork and exec
fork duplicates the current process. After the call there are two nearly identical processes, with the same memory (shared intelligently until one writes to it) and the same open files. The only difference is the value returned by the call: the parent gets the child's identifier, and the child gets zero. That way each one knows who it is.
exec replaces the current process's program with another. It doesn't create anything: the identifier remains the same, what changes is the code being executed.
When your shell runs a command, it does exactly that: it copies itself with fork, and in the copy it calls exec with the program you requested.
It seems convoluted and is very powerful: between fork and exec, the child can change things (redirect its input and output, change user, adjust limits). That's where shell pipes and redirections are implemented.
Zombies, which are poorly explained everywhere
When a process ends, it doesn't disappear completely. The kernel keeps a minimal entry with its exit code, waiting for the parent to read it with wait.
That intermediate state is a zombie.
Two important clarifications:
A zombie consumes nothing. It has no memory, no CPU, no open files. It is an entry in a table. The only thing it retains is its identifier number.
You cannot kill a zombie. It is already dead. Sending it a signal does nothing. The way to eliminate it is for the parent to read its status, or for the parent to terminate.
So, why are they a problem? Because if a program creates many children and never calls wait, zombies accumulate and can exhaust the process table. The symptom is "cannot create more processes" with the machine apparently idle.
And the solution is never to kill zombies: it is to fix the parent.
Orphans, which are the opposite
If the parent terminates before the child, the child becomes an orphan. The kernel immediately assigns it to init (process 1), which is programmed to read the status of any child that terminates.
That is why an orphan never becomes a zombie: init reaps it. It is also the mechanism by which a process "detaches" to run permanently in the background.
Why PID 1 is special, and your container suffers from it
Process 1 has different rules, and there is one that surprises everyone:
For process 1, signals have no default action.
For any other process, receiving the termination signal terminates it, even if its code does nothing about it. For process 1, if the program did not explicitly install a handler, the signal is ignored.
And in a container, your application is usually process 1.
Practical consequence: you send the stop command to the container, your application doesn't handle the signal, nothing happens. The system waits ten seconds and kills it forcefully. That's why every stop takes exactly ten seconds and connections are cut abruptly.
Two solutions:
- Handle the signal in your code and shut down gracefully: stop accepting new connections, finish the ones in progress, close the database, exit.
- Use a minimal init as process 1 (Docker's --init option, or tini). That init handles signals, forwards them to your process, and also reaps orphans.
It's probably the process detail that has the most impact on day-to-day work and is explained the least.

