Detach child process from parent
Asked Answered
H

3

15

When a child process forked from a parent dies, it still exists to some extent and is left in a zombie state, until it is reaped from a wait() call.

Is is possible to detach this parent-child relationship and have the children be automatically reaped? Maybe orphan off the child to init, or something?

Possible use case: Creating a lot of fire-and-forget processes in a long-lived program, without "holding" on to an increasing list of zombie PIDs that cannot be recycled by the OS.

Hervey answered 7/10, 2017 at 14:44 Comment(0)
M
7

Per the POSIX _exit() documentation:

If the parent process of the calling process has set its SA_NOCLDWAIT flag or has set the action for the SIGCHLD signal to SIG_IGN:

  • The process' status information (see Status Information), if any, shall be discarded.

  • The lifetime of the calling process shall end immediately. If SA_NOCLDWAIT is set, it is implementation-defined whether a SIGCHLD signal is sent to the parent process.

  • If a thread in the parent process of the calling process is blocked in wait(), waitpid(), or waitid(), and the parent process has no remaining child processes in the set of waited-for children, the wait(), waitid(), or waitpid() function shall fail and set errno to [ECHILD].

Otherwise:

  • Status information (see Status Information) shall be generated.

  • The calling process shall be transformed into a zombie process. Its status information shall be made available to the parent process until the process' lifetime ends.

  • ...

In short, to prevent child processes from becoming zombie processes, the simplest way is to call sigignore( SIGCHLD );

UPDATE

That does impact the ability to wait for any child process, which may not be desired. The setsid() library function allows a process to disassociate itself from its parent:

pid_t child = fork();
if ( ( pid_t ) 0 == child )
{
    int rc = setsid();

    rc = execv(...);
}
else ...

The disassociated child process doesn't create a zombie nor send SIGCHLD to the parent process on my installed instance of Solaris 11.2.

This is an abbreviated daemonization of a "fire-and-forget" child process, only doing what is necessary to prevent creating a zombie or sending SIGCHLD to the parent process. For a much fuller daemonization procedure, see Linux daemonize

Montespan answered 7/10, 2017 at 14:58 Comment(5)
Thanks, do you know of any solution that doesn't change global behaviour in a program? This solution will change other unknowing parts of the program that might want to synchronize with their children.Hervey
@Hervey See the update. You should be able to use setsid() after fork() and before execv*() to allow the child process to "hide" from the parent, and not create a zombie nor send SIGCHLD on termination.Montespan
Very nice, thanks. My last wish is to be able to do this from the parent at an arbitrary stage in the child's lifetime, but I suppose that's not really possible.Hervey
@Hervey setsid() is async-signal-safe, so you can simply create a signal handler function in your child process code that calls setsid() on a signal you chose, such as SIGUSR1, or one of the real-time signals SIGRTMIN + n. Then, whenever you want to prevent a child process from sending the parent SIGCHLD, just call kill( pid, SIGUSR1 ); to have the child process call its signal handler.Montespan
The update is incorrect. setsid won't prevent the child from being reapable / from becoming a zombie. It'll only serve to prevent ps, in its default settings, from showing such a zombified child. But it'll still be a zombie nonetheless (and ps still can show it as such, e.g., if you give ps the child's exact pid).Orren
O
1

Ignoring SIGCHLD or installing a sighandler for SIGCHLD with the SA_NOCLDWAIT flag on prevents all children from becoming zombies (/ being wait-able).

To do it for a single child, you can either use Linux's clone() without the SIGCHLD flag or you can double fork (or vfork if you know how to do that safely) when starting the child. The child can then immediately _exit(0) and be wait-ed upon by its parent and the grandchild can go on to do the actual work (reparented to init as you say).

(A slight extension of the double fork would be full demonization where the child or the grandchild also calls setsid to create a new session, a new process group and to detach from the controlling terminal. Setsid alone won't prevent the child from becoming a zombie. It will just hide the zombie from ps in its default settings where ps only shows processes in the current terminal-controlling sesssion.)

Orren answered 26/9, 2020 at 22:39 Comment(0)
P
-1

As of Python 3.2, you can use subprocess.Popen() and pass start_new_session=True to accomplish this.

The docs state:

If start_new_session is true the setsid() system call will be made in the child process prior to the execution of the subprocess. (POSIX only)

https://docs.python.org/3/library/subprocess.html#subprocess.Popen

Note that this does not allow one "to do this from the parent at an arbitrary stage in the child's lifetime" as mentioned in a follow-up comment.

Placido answered 6/2, 2020 at 21:26 Comment(1)
We're talking about C, not Python.Ischia

© 2022 - 2024 — McMap. All rights reserved.