【问题标题】:Detach child process from parent从父进程中分离子进程
【发布时间】:2018-03-19 04:31:09
【问题描述】:

当从父进程派生的子进程死亡时,它在一定程度上仍然存在并处于僵尸状态,直到从 wait() 调用中恢复。

是否可以分离这种父子关系并让孩子自动收割?可能是孤儿了,还是什么的?

可能的用例:在一个长期存在的程序中创建大量即发即弃的进程,而不是“持有”越来越多的无法被操作系统回收的僵尸 PID。

【问题讨论】:

    标签: unix posix child-process


    【解决方案1】:

    the POSIX _exit() documentation:

    如果调用进程的父进程已经设置了它的 SA_NOCLDWAIT 标志或已将 SIGCHLD 信号的操作设置为 SIG_IGN:

    • 进程的状态信息(见状态信息),如果有的话,应该被丢弃。

    • 调用进程的生命周期应立即结束。如果设置了SA_NOCLDWAIT,则由实现定义是否SIGCHLD 信号被发送到父进程。

    • 如果调用进程的父进程中的某个线程在wait()waitpid()waitid()中被阻塞,并且父进程有 在等待的子集中没有剩余子进程, wait()waitid()waitpid() 函数将失败并设置 errno 到 [ECHILD]。

    否则:

    • 应生成状态信息(参见状态信息)。

    • 调用进程应转化为僵尸进程。其状态信息应提供给父进程 直到进程的生命周期结束。

    • ...

    总之,为了防止子进程变成僵尸进程,最简单的方法就是调用sigignore( SIGCHLD );

    更新

    这确实会影响等待任何子进程的能力,这可能是不希望的。 setsid() library function 允许进程将自己与其父进程解除关联:

    pid_t child = fork();
    if ( ( pid_t ) 0 == child )
    {
        int rc = setsid();
    
        rc = execv(...);
    }
    else ...
    

    在我安装的 Solaris 11.2 实例上,解除关联的子进程不会创建僵尸进程,也不会向父进程发送 SIGCHLD

    这是“即发即弃”子进程的缩写守护进程,仅执行必要的操作以防止创建僵尸或将SIGCHLD 发送到父进程。有关更完整的守护程序,请参阅Linux daemonize

    【讨论】:

    • 谢谢,您知道任何不会改变程序中全局行为的解决方案吗?此解决方案将更改程序中可能希望与其子进程同步的其他未知部分。
    • @Shaggi 查看更新。您应该能够在fork() 之后和execv*() 之前使用setsid() 以允许子进程对父进程“隐藏”,而不是创建僵尸或在终止时发送SIGCHLD
    • 非常好,谢谢。我的最后一个愿望是能够在孩子一生中的任意阶段从父母那里做到这一点,但我认为这实际上是不可能的。
    • @Shaggi setsid() 是异步信号安全的,因此您可以简单地在子进程代码中创建一个信号处理函数,在您选择的信号上调用 setsid(),例如 SIGUSR1 ,或实时信号之一SIGRTMIN + n。然后,当您想阻止子进程发送父进程SIGCHLD 时,只需调用kill( pid, SIGUSR1 ); 让子进程调用其信号处理程序。
    • 更新不正确。 setsid 不会阻止孩子被收割/变成僵尸。它只会在默认设置中阻止ps 显示这样一个僵尸孩子。但它仍然是一个僵尸(ps 仍然可以显示它,例如,如果你给 ps 孩子的确切 pid)。
    【解决方案2】:

    从 Python 3.2 开始,您可以使用 subprocess.Popen() 并传递 start_new_session=True 来完成此操作。

    文档状态:

    如果 start_new_session 为真,setsid() 系统调用将在子进程执行之前在子进程中进行。 (仅限 POSIX)

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

    请注意,这不允许“在孩子一生中的任意阶段从父母那里做到这一点”,如后续评论中所述。

    【讨论】:

    • 我们谈论的是 C,而不是 Python。
    【解决方案3】:

    忽略SIGCHLD 或为SIGCHLD 安装一个带有SA_NOCLDWAIT 标志的sighandler 可防止所有孩子成为僵尸(/成为wait-able)。

    要为单个孩子执行此操作,您可以在启动孩子时使用不带 SIGCHLD 标志的 Linux 的 clone(),或者您可以加倍 fork(或 vfork,如果您知道如何安全地这样做) .然后孩子可以立即_exit(0) 并被其父母wait-ed,孙子可以继续做实际的工作(如你所说,重新成为init)。

    (双叉的轻微扩展将是完全妖魔化,其中子或孙子调用setsid来创建新会话、新进程组并与控制终端分离。setsid单独不会阻止孩子成为僵尸。它只会在默认设置中对ps 隐藏僵尸,而ps 仅显示当前终端控制会话中的进程。)

    【讨论】:

      猜你喜欢
      • 2015-04-12
      • 2016-03-05
      • 2023-03-24
      • 1970-01-01
      • 2012-05-05
      • 2017-08-09
      • 2011-07-16
      • 2021-02-26
      • 1970-01-01
      相关资源
      最近更新 更多