[TUHS] fork
Steffen Nurpmeso via TUHS
tuhs at tuhs.org
Wed May 13 00:18:48 AEST 2026
rob--- via TUHS wrote in
<C5DC67A1-A2DA-40F9-A128-8D84A2B3068B at atvetsystems.com>:
|> On 12 May 2026, at 12:18, Peter Jeremy via TUHS <tuhs at tuhs.org> wrote:
|>
|> On 2026-May-12 10:19:25 +0200, Folkert van Heusden via TUHS <tuhs at tuhs.o\
|> rg> wrote:
|>> Now something unexpected is happening: after a request, a "<defunct>" \
|>> proces
|>> is left in the process list. Usually that is caused by a fork() \
|>> not being
|>> join()ed afterwards. But in main() of httpdp I invoke signal(SIGCHLD,
|>> SIG_IGN); so I thought I took care of that?
|>
|> For "modern" Unixes (e.g. FreeBSD and Linux), there's a SA_NOCLDWAIT flag
|> for sigaction(2) that explicitly says setting SIGCHLD to SIG_IGN \
|> means that
|> child processes will not become zombies but will instead be implicitly
|> reaped immediately.
|>
|> This doesn't exist in BSD2.11 and, as far as I can tell from looking
|> through the sources, there's no equivalent behaviour: When a process \
|> exits,
|> the process becomes a zombie and the parent is sent a SIGCHLD (see
|> kern_exit.c::exit()). In psignal(), if a signal is marked SIG_IGN, the
|> signal is completely ignored - there's no special handling of SIGCHLD.
|> There's no obvious code to clean up zombie processes if the parent \
|> doesn't
|> wait() for them.
|I remember having to use a double fork() in the 90’s which I think \
|maybe to solve a problem like this that the top process can fork() \
|and wait() but the child fork(’s again and exits so that the death \
|of the grandchild process gets cleaned by by init.
In a communication this year a friendly Russian said
By the way, I thought that double fork is something useless for dozens
of years. As I remember, the second fork() was needed because of
relatively old SysV-like systems still gave more than enough permissions
to the child process after the first fork(). As I remember Richard Stevens
book, BSDs never required second fork. fork()+setsid() were enough. But
I guess maybe Linux inherited SysV-like behaviour/issue and that is why
second fork() is needed.
and he refers to advanced_pogramming_in_the_unix_environment,
13.3, Coding Rules, on daemonization:
2. Call fork and have the parent exit. This does several
things. First, if the daemon was started as a simple shell
command, having the parent terminate makes the shell think that
the command is done. Second, the child inherits the process
group ID of the parent but gets a new process ID, so we're
guaranteed that the child is not a process group leader. This is
a prerequisite for the call to setsid that is done next.
3. Call setsid to create a new session. The three steps listed
in Section 9.5 occur. The process (a) becomes a session leader
of a new session, (b) becomes the process group leader of a new
process group, and (c) has no controlling terminal.
Under System V–based systems, some people recommend calling fork
again at this point and having the parent terminate. The second
child continues as the daemon. This guarantees that the daemon
is not a session leader, which prevents it from acquiring
a controlling terminal under the System V rules (Section
9.6). Alternatively, to avoid acquiring a controlling terminal,
be sure to specify O_NOCTTY whenever opening a terminal device.
[Which in turn is
POSIX.1 leaves the choice of the mechanism used to allocate
a controlling terminal up to each individual
implementation. We'll show the actual steps in Section 19.4.
Systems derived from UNIX System V allocate the controlling
terminal for a session when the session leader opens the first
terminal device that is not already associated with
a session. This assumes that the call to open by the session
leader does not specify the O_NOCTTY flag (Section 3.3).
BSD-based systems allocate the controlling terminal for
a session when the session leader calls ioctl with a request
argument of TIOCSCTTY (the third argument is a null
pointer). The session cannot already have a controlling
terminal for this call to succeed. (Normally, this call to
ioctl follows a call to setsid, which guarantees that the
process is a session leader without a controlling terminal.)
The POSIX.1 O_NOCTTY flag to open is not used by BSD-based
systems, except in compatibility-mode support for other
systems.]
(He mentioned this, i think, because i do double fork in some
stupid little thing (greylister), but this is because of Linux and
FreeBSD sandboxing .. and the "necessity" to separate the actual
syslog interaction, on Linux mostly because of who knows what
syslog internally needs .. and fd is opened delayed etc etc etc.
OpenBSD has added a syslog system call which makes that topic
easy. Btw, and imho, Linux seccomp should really have a way to
simply declare a bitmask of allowed or forbidden system calls,
instead of these lengthy tit-and-tat things. Maybe landlock,
.. but this is off topic, of course.)
New FreeBSD and Linux offer possibilities to declare the "reaper"
process to which PIDs get reparented. FreeBSD with iteration
over, and all that. (Then again this is not well integrated in
respect to P(luggable)A(uthentication)M(odules), which is pretty
well used on Linux, FreeBSD, NetBSD, and offers sessions, but any
idiot -- even myself that is -- can "escape" from these sessions
with a simple I/O-redirected shell command, effectively ending up
with init(8), because they do not use this reaper functionality.
On Linux, you have that systemd monster, which surely does this
the right way, on top, say. There is finit, i think it also tries
to go cgroup + X, but in how far it does X, aka capabilities,
overlay mounts (for eg /tmp/), i would have to look again. BSDs,
anyway not. The mentioned guy swears on s6 / runit, which is then
also some approach, more UNIX-like, that much is plain. Puh.
--steffen
|
|Der Kragenbaer, The moon bear,
|der holt sich munter he cheerfully and one by one
|einen nach dem anderen runter wa.ks himself off
|(By Robert Gernhardt)
More information about the TUHS
mailing list