fork() создаёт дочерний процесс — копию родителя, но память не копируется физически, а размечается как copy-on-write. exec() затем заменяет образ процесса новым бинарником, сохраняя PID и открытые file descriptors. Именно эта пара — то, как shell запускает любую команду.