La fork() crea un clone del processo correntemente in esecuzione; uno dei due (quello a cui la fork restituisce un pid) diventa il processo "padre", l'altro è figlio di quest'ultimo (e spesso userà una qualche variante della exec per caricare il codice di un altro programma da eseguire).

Ovviamente questo porta al formarsi di una gerarchia (ad albero) di processi, in cima alla quale c'è il processo init, che è il primo processo user-mode del sistema; esso ha PID 1, è sempre in esecuzione, si occupa di avviare/terminare tutti gli altri processi fondamentali all'avvio e ai cambiamenti di runlevel e adotta i processi orfani (su questo torno dopo).

Quello che accade di solito è che il processo padre, ad un certo punto, attenderà la terminazione del processo figlio usando la syscall wait; questa attende che il processo figlio termini e fornisce al processo padre il codice di uscita.
Uno scenario di questo genere si ha ad esempio in una shell: quando esegui un comando (che so, ls) la shell fa una fork; il processo figlio fa una exec per eseguire effettivamente il programma richiesto al suo interno, mentre il processo padre (la shell) farà una wait sul pid del figlio, bloccando l'esecuzione finché il figlio non ha terminato.

Rispetto a questo scenario "classico" possono capitare fondamentalmente due "deviazioni":
- il processo figlio termina prima che il processo padre chiami la wait su di esso; si ha un processo zombie;
- il processo padre termina prima del processo figlio; si ha un processo orfano.

Come detto, un processo zombie è un processo che ha terminato la sua esecuzione, ma che non è stato "atteso" (tramite la wait) dal processo padre; per tutti i fini pratici è ormai un processo "morto" (dato che è terminato non ha più bisogno di tempo di CPU, di spazio di indirizzi virtuale, tabelle di descrittori file, ...), ma sopravvive nella tabella dei processi del kernel perché il processo padre ha ancora il "diritto" di fare una wait su di esso (per ottenerne il codice di uscita e altre informazioni sullo stato del processo figlio nel momento in cui è terminato); nel momento in cui il processo padre effettua la wait, gli viene fornito il codice di uscita, il processo zombie sparisce (perché non serve più a niente) e il suo PID può essere riciclato per altri processi.

Se invece il processo padre termina prima che il figlio abbia terminato, il processo figlio diventa un "orfano", e viene "adottato" da init - ovvero, il suo processo padre diventa init; init quindi si occupa di chiamare la wait sui processi orfani, in maniera tale da evitare che questi quando terminano diventino indefinitamente zombie (se fossero privi di un processo padre non ci sarebbe modo di chiamare la wait su di essi, e, una volta terminati, resterebbero indefinitamente zombie).

Questo è di base come funziona la questione sui sistemi di famiglia Unix, poi di fatto ci sono un po' di varianti sul tema, a cui si accenna negli articoli di Wikipedia su processi zombie e processi orfani, e/o nella documentazione del sistema operativo particolare in uso (in particolare vedi la manpage di wait & co.).