从 parent 分离 child 进程
Detach child process from parent
当从 parent 派生的 child 进程死亡时,它在某种程度上仍然存在并处于僵尸状态,直到它从 wait() 调用中回收。
是否可以分离这种 parent-child 关系并让 children 自动收割?也许将 child 孤立起来进行初始化,或者什么?
可能的用例:在 long-lived 程序中创建大量 fire-and-forget 进程,而不 "holding" 增加无法被 [= 回收的僵尸 PID 列表24=].
根据 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.
...
总之,要防止子进程成为僵尸进程,最简单的方法就是调用sigignore( SIGCHLD );
更新
这确实会影响等待 any 子进程的能力,这可能是不希望的。 setsid()
library function 允许进程将自身与其父进程分离:
pid_t child = fork();
if ( ( pid_t ) 0 == child )
{
int rc = setsid();
rc = execv(...);
}
else ...
在我安装的 Solaris 11.2 实例上,取消关联的子进程不会创建僵尸,也不会发送 SIGCHLD
到父进程。
这是 "fire-and-forget" 子进程的缩写守护进程,仅执行必要的操作以防止创建僵尸或向父进程发送 SIGCHLD
。有关更完整的守护进程,请参阅 Linux daemonize
从 Python 3.2 开始,您可以使用 subprocess.Popen() 并传递 start_new_session=True 来完成此操作。
文档状态:
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
请注意,这不允许 "to do this from the parent at an arbitrary stage in the child's lifetime" 如后续评论中所述。
忽略SIGCHLD
或为SIGCHLD
安装一个带有SA_NOCLDWAIT
标志的sighandler可以防止所有children变成僵尸(/成为wait
-能)。
要为单个 child 执行此操作,您可以使用 Linux 的 clone()
而不使用 SIGCHLD
标志,或者您可以加倍 fork
(或者 vfork
如果你知道如何安全地做到这一点)在启动 child 时。然后 child 可以立即 _exit(0)
并被其 parent 编辑 wait
并且 grandchild 可以继续进行实际工作(重新 parent按你说的改成了init
)。
(双叉的轻微扩展将完全妖魔化 child 或 grandchild also 调用 setsid 来创建一个新会话, 一个新的进程组并从控制终端分离。Setsid 单独不会阻止 child 成为僵尸。它只会在其默认设置中隐藏 ps
的僵尸,其中 ps
仅显示当前 terminal-controlling 会话中的进程。)
当从 parent 派生的 child 进程死亡时,它在某种程度上仍然存在并处于僵尸状态,直到它从 wait() 调用中回收。
是否可以分离这种 parent-child 关系并让 children 自动收割?也许将 child 孤立起来进行初始化,或者什么?
可能的用例:在 long-lived 程序中创建大量 fire-and-forget 进程,而不 "holding" 增加无法被 [= 回收的僵尸 PID 列表24=].
根据 the POSIX _exit() documentation:
If the parent process of the calling process has set its
SA_NOCLDWAIT
flag or has set the action for theSIGCHLD
signal toSIG_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 aSIGCHLD
signal is sent to the parent process.If a thread in the parent process of the calling process is blocked in
wait()
,waitpid()
, orwaitid()
, and the parent process has no remaining child processes in the set of waited-for children, thewait()
,waitid()
, orwaitpid()
function shall fail and seterrno
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.
...
总之,要防止子进程成为僵尸进程,最简单的方法就是调用sigignore( SIGCHLD );
更新
这确实会影响等待 any 子进程的能力,这可能是不希望的。 setsid()
library function 允许进程将自身与其父进程分离:
pid_t child = fork();
if ( ( pid_t ) 0 == child )
{
int rc = setsid();
rc = execv(...);
}
else ...
在我安装的 Solaris 11.2 实例上,取消关联的子进程不会创建僵尸,也不会发送 SIGCHLD
到父进程。
这是 "fire-and-forget" 子进程的缩写守护进程,仅执行必要的操作以防止创建僵尸或向父进程发送 SIGCHLD
。有关更完整的守护进程,请参阅 Linux daemonize
从 Python 3.2 开始,您可以使用 subprocess.Popen() 并传递 start_new_session=True 来完成此操作。
文档状态:
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
请注意,这不允许 "to do this from the parent at an arbitrary stage in the child's lifetime" 如后续评论中所述。
忽略SIGCHLD
或为SIGCHLD
安装一个带有SA_NOCLDWAIT
标志的sighandler可以防止所有children变成僵尸(/成为wait
-能)。
要为单个 child 执行此操作,您可以使用 Linux 的 clone()
而不使用 SIGCHLD
标志,或者您可以加倍 fork
(或者 vfork
如果你知道如何安全地做到这一点)在启动 child 时。然后 child 可以立即 _exit(0)
并被其 parent 编辑 wait
并且 grandchild 可以继续进行实际工作(重新 parent按你说的改成了init
)。
(双叉的轻微扩展将完全妖魔化 child 或 grandchild also 调用 setsid 来创建一个新会话, 一个新的进程组并从控制终端分离。Setsid 单独不会阻止 child 成为僵尸。它只会在其默认设置中隐藏 ps
的僵尸,其中 ps
仅显示当前 terminal-controlling 会话中的进程。)