Bizzare Unix semantics question. Given the parent of init is init (getppid(() of pid 1 is 1) is init the child of init, and if only init is running what is the correct return from wait(NULL);
Believe it or not I actually found a case it matters in my Fuzix init today and having read various docs I'm really not sure what the answer is. ECHILD or blocking ?
@etchedpixels I doubt you find any system that doesn't return ECHILD?
@leah Unix V7 seems to sleep.
@etchedpixels does it have waitpid already? can you waitpid(1, &status, 0)?
@leah V7 has wait() only not waitpid(). The dozen other wait() with extras before waitpid() are all later things
@etchedpixels it sleeps indeed, the code is easy to check. I'm very sure Linux does not.
@leah @etchedpixels POSIX seems to require wait() to sleep until a signal or status information is available
ECHILD is returned if there's no child processes
@leah @etchedpixels ...very awkwardly, i completely forgot about the "process is its own parent" thing halfway thru
so after that blunder i checked and wait(0) on pid1 on linux returns ECHILD (unexpectedly to me, after staring at the code), tho i can see reason for both behaviors (and like. how many people wrote custom inits for v7 unix)
also with this being pid1 posix doesn't apply.. i think this is very much a "implementation defined" behavior (and i think linux might be the only system where you are reasonably meant to swap out the init anyways?)
@puckipedia @leah Best answer we have (off public) so far is from Rationale of waitpid in POSIX.
"A call to the wait() or waitpid() function only returns status on an immediate child process of the calling process; that is, a child that was produced by a single fork() call (perhaps followed by an exec or other function calls) from the parent"
And init didn't create init by fork.... so I guess it's self parenting but not its own child !
Which means the Fuzix kernel has a bug.
@etchedpixels @leah yeah, and i agree that's probably the most useful parse (inherently, pid 1 _is_ the root process, so it can't have a parent; and also, if it terminates, it doesn't get a SIGCHILD for itself, sooo)
@puckipedia @etchedpixels my init kills everything on shutdown and reaps until ECHILD appears, so this behavior would break it.
@puckipedia @etchedpixels actually I have a timeout, so it's fine
@etchedpixels I think it changed in 4.3 when https://github.com/dspinellis/unix-history-repo/commit/baffb38b8143b1a9fb9b8f85315071b0931617fd introduced an explicit list of child processes, where pid 1 doesn't include itself.
@leah @etchedpixels oh! unix v7 has no SIGCHLD (which is how you'd figure out a child has exited, ; and also, wait() in unix v7 is specified to wait for any child to exit _or_ a signal is received, so that makes sense
@etchedpixels (Voluntarily!) paging @dalias, who probably has an idea. I'm curious about this too and have NFI.
@etchedpixels just dropped in to say I love this whole thread and exploring weirdo niche system internals stuff, one of my favorite things about Mastodon that reminds me of Usenet, mailing lists, and IRC.
@etchedpixels @leah on Linux, pid 1's ppid is 0, always (container or not). 0 is considered the pid of the kernel, any process that's created by the kernel directly rather than spawned from a userspace process has ppid 0. You can test this e.g. with the old hotplug mechanism, /proc/sys/kernel/hotplug, which spawns a program on uevents (like plugging a device).
I always wait with poll() (on a self-pipe catching SIGCHLD) followed by looping around waitpid(-1, NULL, WNOHANG), rather than wait(NULL), because it's *never* the case that a process that needs to wait for children it doesn't know it has can loop on doing this unconditionally without listening to other events.
@ska @etchedpixels @leah Same on 6th Edition UNIX: parent of pid 1 is 0.
@ska @etchedpixels @leah It was also not uncommon for such ancient systems to have alternative recovery boot media where init itself was /bin/sh. On WSL Idris (clean-room reimplementation of 6th Ed), there was even the option of having the half-shell (hsh) as the init process (pid 1). hsh was a cut-down shell that couldn't fork, only exec, and was automatically reloaded afresh by the kernel after every invoked external process exited. Useful for extremely limited-memory systems or when most of the system hardware was playing-up.
@ska @etchedpixels @leah Summary: optional alternative (switchable) init programs have been a thing since at least 1975, maybe earlier.
@etchedpixels Oh thanks for that, gonna bug me all day now.
My instinct says ECHILD but I have no support. I think the init is it's own parent thing is simply to stop various things falling out of the bottom of the ancestral tree.
Init is the ancestor of all processes except itself. Hmm...
@etchedpixels Interesting question and I would have an opinion if I had done child reaping in ubttix. I believe init will return ECHILD (will not wait itself) because thats more useful, but thats just from the top of my head.