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?
-
2Traditional forkbomb: while(1) fork(); to hog system resources.– JoshuaCommented Oct 18, 2017 at 21:21
4 Answers
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_SSH
(though 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)
The 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 fork()
without 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 dhclient
, php-fpm
, and urxvtd
.
A program that calls exec()
without 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:
chpst
from Gerrit Pape's runits6-softlimit
ands6-envdir
from Laurent Bercot's s6local-reaper
andmove-to-control-group
from my nosh toolsetrdprio
andidprio
on FreeBSDnumactl
on FreeBSD and Linux
The daemontools family toolsets have a lot of such tools, from machineenv
through find-matching-jvm
to runtool
.
In addition to other answers, debuggers, using ptrace
, typically make use of the gap between fork
and 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
with PTRACE_TRACEME
and then call exec
. Whichever program the child exec's will now be traceable by the parent.
-
There are many examples of doing things between fork and exec, the most common being redirecting I/O (e.g. setting up pipes). But the question is about doing fork without any exec at all.– BarmarCommented Oct 25, 2017 at 18:54
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.
-
It's actually very few dæmons nowadays, with most not doing this as standard or having a commonly-used mode not to. It is a mistake to fork leading to readiness mismatch and horrors, and is one of the mistakes of the dæmonization fallacy. This mistake has at long last widely fallen out of favour.– JdeBPCommented Oct 19, 2017 at 20:26