I'm learning about fork() and exec() commands. It seems like fork() and exec() are usually called together. (fork() creates a new child process, and exec() replaces the current process image with a new one.) However, in what scenarios might you call each function on its own? Are there scenarios like these?
Sure! A common pattern in "wrapper" programs is to do various things and then replace itself with some other program with only an
exec call (no fork)
#!/bin/sh export BLAH_API_KEY=blub ... exec /the/thus/wrapped/program "$@"
A real-life example of this is
git(1) does also offer
GIT_SSH_COMMAND if you do not want to do the above wrapper program method).
Fork-only is used when spawning a bunch of typically worker processes (e.g. Apache
httpd in fork mode (though fork-only better suits processes that need to burn up the CPU and not those that twiddle their thumbs waiting for network I/O to happen)) or for privilege separation used by
sshd and other programs on OpenBSD (no exec)
$ doas pkg_add pstree ... $ pstree | grep sshd |-+= 70995 root /usr/sbin/sshd | \-+= 28571 root sshd: jhqdoe [priv] (sshd) | \-+- 14625 jhqdoe sshd: jhqdoe@ttyp6 (sshd)
root sshd has on client connect forked off a copy of itself (28571) and then another copy (14625) for the privilege separation.
There are plenty.
Programs that call
exec() are usually following a pattern of spawning child worker processes for performing various tasks in separate processes to the main one. You'll find this in programs as varied as
A program that calls
fork() is chain loading, overlaying its process with a different program image. There is a whole subculture of chain loading utilities that do particular things to process state and then execute another program to run with that revised process state. Such utilities are common in the daemontools family of service and system management toolsets, but are not limited to those. A few examples:
chpstfrom Gerrit Pape's runit
s6-envdirfrom Laurent Bercot's s6
move-to-control-groupfrom my nosh toolset
numactlon FreeBSD and Linux
In addition to other answers, debuggers, using
ptrace, typically make use of the gap between
exec. A debuggee should mark itself with
PTRACE_TRACEME to indicate that it is being traced by its parent process - the debugger. This is to give required permissions to the debugger.
So a debugger would first fork itself. The child would call
PTRACE_TRACEME and then call
exec. Whichever program the child exec's will now be traceable by the parent.
exec without fork
There are at least two reasons why you would want to do such a thing:
- Chain loading. The current process image is replaced with something different.
- Restarting the currently running program (might for example happen when you SIGHUP or such a server process, reloading everything and doing a completely fresh start). In some way, one could argue this is chain loading, only coincidentially with the same program.
fork without exec
That's what every daemon does every time it's started (twice, indeed). This does several things, among them the shell doesn't hang (since the original process that the shell waits on terminates) and the daemon is no longer controlled by the terminal, so closing the shell window doesn't kill the daemon.
Another common use is forking worker children, which was made famous by the apache web server some 25 years ago (nowadays this isn't considered state of the art any more due to being very prone to the thundering herd problem, but it sure provides the darn simplest, most robust server possible).
Yet another common use is to create a consistent snapshot.
fork not only creates a process, it also copies (in theory, in reality it only marks pages copy-on-write) the address space. This (atomically) creates a snapshot of the complete program data which the parent can no longer modify.
Some programs take advantage of that. For example redis saves data to disk (in a consistent state) while at the same time modifying the data set concurrently. This only works because
fork created a consistent snapshot that doesn't see the modifications made by the parent process.