6

In POSIX, processes are “related” to each other through two basic hierarchies:

  1. The hierarchy of parent and child processes.

  2. The hierarchy of sessions and process groups.

User processes have a great deal of control over the latter, via setpgid and setsid, but they have very little control over the former—the parent process ID is set when a process is spawned and altered by the kernel when the parent exits (usually to PID 1), but otherwise it does not change. Reflecting on that, I’ve been wondering how important the parent–child relationship really is.

Here’s a summary of my understanding so far:

  • Parent–child relationships are clearly important from the perspective of the parent process, since various syscalls, like wait and setpgid, are only allowed on child processes.

  • The session–group–process relationship is clearly important to all processes, both the session leader and other processes in the session, since syscalls like kill operate on entire process groups, setpgid can only be used to join a group in the same session, and all processes in a session’s foreground process group are sent SIGHUP if the session leader exits.

  • What’s more, the two hierarchies are clearly related from the perspective of the parent, since setsid only affects new children and setpgid can only be used on children, but they seem essentially unrelated from the perspective of the child (since a parent process dying has no impact whatsoever on a process’s group or session).

Conspicuously absent, however, is any reason for a child process to care what its current parent is. Therefore, I have the following question: does the current value of getppid() have any importance whatsoever from the perspective of the child process, besides perhaps identifying whether or not its spawning process has exited?


To put the same question another way, imagine the same program is spawned twice, from the same parent, in two different ways:

  1. The first child is spawned in the usual way, by fork() followed shortly by exec().

  2. The second child is spawned indirectly: the parent process calls fork(), and then the child also calls fork(), and it’s the grandchild process that calls exec(). The immediate child then exits, so the grandchild is orphaned, and its PPID is reassigned to PID 1.

In this hypothetical scenario, assuming all else is equal, do any reasonable programs have any reason to behave any differently? So far, my conclusion seems to be “no,” since the session is left unchanged, as are the process’s inherited file descriptors… but I’m not sure.

Note: I do not consider “acquiring the parent PID to communicate with it” to be a valid answer to that question, since orphaned programs cannot in general rely on their PPID to be set to 1 (some systems set orphaned processes’ PPID to some other value), so the only way to avoid a race condition is to acquire the parent process ID via a call to getpid() before forking, then to use that value in the child.

3

When I saw this question, I was pretty interested because I know I've seen getppid used before..but I couldn't remember where. So, I turned to one of the projects that I figured has probably used every Linux syscall and then some: systemd. One GitHub search later, and I found two uses that portray some more general use cases (there are a few other uses as well, but they're more specific to systemd):

  • In sd-notify. For some context: systemd needs to know when a service has started so it can proceed to start any that depend on it. This is normally done from a C program via the sd_notify API, which is a way for daemons to tell systemd their status.

    Of course, if you're using a shell script as a service...calling C functions isn't exactly doable. Therefore, systemd comes with the systemd-notify command, which is a small wrapper over the sd_notify API. One problem: systemd also needs to know the PID that is sending the message. For systemd-notify, this would be its own PID, which would be a short-lived process ID that immediately goes away. Not useful.

    You probably already know where I'm headed: getppid is used by systemd-notify to grab the parent process's PID, since that's usually the actual service process. In short, getppid can be used by a short-lived CLI application to send a message on behalf of the parent process.

    Once I found this, another unix tool that might use getppid like this came to mind: polkit, which is a process authentication framework used to gate stuff like sending D-Bus messages or running privileged applications. (At minimum, I'd guess you've seen the GUI password prompts that are displayed by polkit's auth agents.) polkit includes an executable named pkexec that can be used a bit like sudo, except now polkit is used for authorization. Now, polkit needs to know the PID of the process asking for authorization...yeah you get the idea, pkexec uses getppid to find that.

    (While looking at that, I also found out that polkit's TTY auth agent uses it too.)

  • This one's a bit less interesting but still notable: getppid is used to emulate PR_SET_PDEATHSIG if the parent had died by the time that flag was set. (The flag is just a way for a child to be automatically sent a signal like SIGKILL if the parent dies.)

| improve this answer | |
  • 1
    This is exactly the kind of answer I was looking for. These are interesting use cases, but they basically confirm to me that the PPID is only really useful for either determining if one’s parent has died or for some kind of best-effort attempt to infer the parent PID when you don’t control the parent process and therefore are unable to have it explicitly coordinate with you when forking. Thanks! – Alexis King Nov 7 '19 at 1:31
1

It seems that the range of scenarios where a process needs any awareness of any other process in a system, such as it's parent, grand-parent, etc. involve process management and/or inter-process-communication. To further narrow the scope, the range of operations that take a pid_t as an argument, or return a pid_t, are primarily signalling and other process management tasks. Given such a limited range of uses, the only reasons I can think of for using getppid, besides diagnostic information, would be if the child needs to signal the parent, or determine if the parent is still running. For example, mod_md in Apache httpd sends a graceful restart signal to the parent process to trigger a reconfiguration.

While the "Note" on your question describes a good alternative to getppid, it also appears to hint at a possible use for getppid. For context, I was watching a presentation from PWL Conf 2019 last night, On the Expressive Power of Programming Languages, and the definition of "expressiveness" from that paper is biasing my interpretation of the question. The definition boils down to how a program's behavior might be different with or without the presence of a feature.

Starting with the assumptions:

  • A process needs to signal it's parent process for some reason.
  • Different operating systems handle re-parenting orphaned processes differently.

What does it mean if the pre-fork PID does not match getppid? Comparing the pre-fork PID with getppid might actually provide a cross-platform, race-free mechanism for a child process to determine if it has been orphaned. If pre-fork-pid != getppid then the child is orphaned, otherwise it's not.

This does seem like an uncommon thing to need, or something that would likely be written using platform specific compilation, #ifdef, when needed. Also, a similar effect could be achieved using kill 0 on the pre-fork-pid, however this can yield false positives in sufficiently long running processes when the system reuses "old" process IDs.

So the combination of an immutable, pre-fork parent process ID, and a "live" getppid call looks like it might be a highly reliable, cross-platform mechanism for checking on or signalling a parent process.

| improve this answer | |
0

Zombies: This is an edge case, but it is effectful in the sense it impacts whether a program halts or not. The only situation I have encountered where a child process' behavior is different based on which process it is parented to is at the time it exits. When a process exits, SIGCHLD is sent to the ppid of the child. If the parent process is wedged or not handling SIGCHLD, then the child is left in a zombie state until it's exit signal is received. If the ppid of the child changes while it is in a zombie state, by killing the parent process and being re-parented to init, and the SIGCHLD is received, then the child will finish terminating and be reaped.

| improve this answer | |
  • This is a good point, but I don’t think it’s quite the kind of thing I’m looking for, since it still doesn’t change anything from the perspective of the child — it only affects behavior that happens after the child process has exited by definition. Essentially, I’m asking if the child would ever have any reason to call getppid() and do anything meaningful with the result, beyond perhaps to present some very basic diagnostic information. – Alexis King Oct 6 '19 at 14:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.