Linux 父进程退出后如何让子进程死亡?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/284325/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:43:21  来源:igfitidea点击:

How to make child process die after parent exits?

clinuxunixprocessfork

提问by Pawe? Hajdan

Suppose I have a process which spawns exactly one child process. Now when the parent process exits for whatever reason (normally or abnormally, by kill, ^C, assert failure or anything else) I want the child process to die. How to do that correctly?

假设我有一个进程正好产生一个子进程。现在,当父进程因任何原因(正常或异常,通过kill、^C、断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?



Some similar question on stackoverflow:

关于stackoverflow的一些类似问题:



Some similar question on stackoverflow for Windows:

关于Windows 的stackoverflow 的一些类似问题:

采纳答案by qrdl

Child can ask kernel to deliver SIGHUP(or other signal) when parent dies by specifying option PR_SET_PDEATHSIGin prctl()syscall like this:

孩子可以SIGHUP通过PR_SET_PDEATHSIGprctl()系统调用中指定选项来要求内核在父母死亡时传递(或其他信号),如下所示:

prctl(PR_SET_PDEATHSIG, SIGHUP);

prctl(PR_SET_PDEATHSIG, SIGHUP);

See man 2 prctlfor details.

详情请参阅man 2 prctl

Edit: This is Linux-only

编辑:这是仅适用于 Linux 的

回答by Alnitak

I don't believe it's possible to guarantee that using only standard POSIX calls. Like real life, once a child is spawned, it has a life of its own.

我不相信可以保证只使用标准的 POSIX 调用。就像现实生活一样,孩子一旦出生,就有了自己的生活。

It ispossible for the parent process to catch most possible termination events, and attempt to kill the child process at that point, but there's always some that can't be caught.

可能的父进程捕捉最有可能终止事件,并试图在该点杀子过程,但总有一些不能被捕获。

For example, no process can catch a SIGKILL. When the kernel handles this signal it will kill the specified process with no notification to that process whatsoever.

例如,没有进程可以捕获SIGKILL. 当内核处理此信号时,它将终止指定的进程,而不会通知该进程。

To extend the analogy - the only other standard way of doing it is for the child to commit suicide when it finds that it no longer has a parent.

扩展类比 - 唯一的其他标准方法是让孩子在发现不再有父母时自杀。

There is a Linux-only way of doing it with prctl(2)- see other answers.

有一种仅适用于 Linux 的方法prctl(2)- 请参阅其他答案。

回答by MarkR

Does the child process have a pipe to/from the parent process? If so, you'd receive a SIGPIPE if writing, or get EOF when reading - these conditions could be detected.

子进程是否有进出父进程的管道?如果是这样,您会在写入时收到 SIGPIPE,或在读取时收到 EOF - 可以检测到这些情况。

回答by Ana Betts

Install a trap handlerto catch SIGINT, which kills off your child process if it's still alive, though other posters are correct that it won't catch SIGKILL.

安装一个陷阱处理程序来捕获 SIGINT,如果它仍然活着,它会杀死您的子进程,尽管其他海报是正确的,它不会捕获 SIGKILL。

Open a .lockfile with exclusive access and have the child poll on it trying to open it - if the open succeeds, the child process should exit

打开具有独占访问权限的 .lockfile 并让子轮询尝试打开它 - 如果打开成功,子进程应该退出

回答by dmckee --- ex-moderator kitten

I have achieved this in the past by running the "original" code in the "child" and the "spawned" code in the "parent" (that is: you reverse the usual sense of the test after fork()). Then trap SIGCHLD in the "spawned" code...

过去,我通过在“子项”中运行“原始”代码和在“父项”中运行“生成”代码来实现这一点(即:在 之后颠倒通常意义上的测试fork())。然后在“生成”代码中捕获 SIGCHLD ...

May not be possible in your case, but cute when it works.

在您的情况下可能无法实现,但是在工作时很可爱。

回答by Jonathan Leffler

Under POSIX, the exit(), _exit()and _Exit()functions are defined to:

POSIXexit()_exit()_Exit()函数被定义为:

  • If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.
  • 如果该进程是一个控制进程,SIGHUP 信号将被发送到属于调用进程的控制终端的前台进程组中的每个进程。

So, if you arrange for the parent process to be a controlling process for its process group, the child should get a SIGHUP signal when the parent exits. I'm not absolutely sure that happens when the parent crashes, but I think it does. Certainly, for the non-crash cases, it should work fine.

因此,如果您将父进程安排为其进程组的控制进程,则子进程应该在父进程退出时收到 SIGHUP 信号。我不确定当父母崩溃时会发生这种情况,但我认为确实如此。当然,对于非崩溃情况,它应该可以正常工作。

Note that you may have to read quite a lot of fine print - including the Base Definitions (Definitions) section, as well as the System Services information for exit()and setsid()and setpgrp()- to get the complete picture. (So would I!)

请注意,您可能需要阅读大量细则——包括基本定义(定义)部分,以及系统服务信息exit()setsid()setpgrp()——以获得完整的图片。(我也会!)

回答by Schof

I'm trying to solve the same problem, and since my program must run on OS X, the Linux-only solution didn't work for me.

我正在尝试解决同样的问题,并且由于我的程序必须在 OS X 上运行,因此仅适用于 Linux 的解决方案对我不起作用。

I came to the same conclusion as the other people on this page -- there isn't a POSIX-compatible way of notifying a child when a parent dies. So I kludged up the next-best thing -- having the child poll.

我得出了与此页面上的其他人相同的结论——当父母去世时,没有一种与 POSIX 兼容的方式来通知孩子。所以我拼凑了下一个最好的事情——让孩子进行民意调查。

When a parent process dies (for any reason) the child's parent process becomes process 1. If the child simply polls periodically, it can check if its parent is 1. If it is, the child should exit.

当父进程(出于任何原因)死亡时,子进程的父进程将成为进程 1。如果子进程只是定期轮询,它可以检查其父进程是否为 1。如果是,则子进程应退出。

This isn't great, but it works, and it's easier than the TCP socket/lockfile polling solutions suggested elsewhere on this page.

这不是很好,但它有效,并且比本页其他地方建议的 TCP 套接字/锁文件轮询解决方案更容易。

回答by Phil Rutschman

If you're unable to modify the child process, you can try something like the following:

如果您无法修改子进程,您可以尝试以下操作:

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

This runs the child from within a shell process with job control enabled. The child process is spawned in the background. The shell waits for a newline (or an EOF) then kills the child.

这会从启用了作业控制的 shell 进程中运行子进程。子进程在后台生成。shell 等待换行符(或 EOF)然后杀死孩子。

When the parent dies--no matter what the reason--it will close its end of the pipe. The child shell will get an EOF from the read and proceed to kill the backgrounded child process.

当父节点死亡时——不管是什么原因——它会关闭管道的末端。子 shell 将从读取中获得 EOF 并继续杀死后台子进程。

回答by Yefei

I think a quick and dirty way is to create a pipe between child and parent. When parent exits, children will receive a SIGPIPE.

我认为一种快速而肮脏的方法是在孩子和父母之间创建一个管道。当父母退出时,孩子将收到一个 SIGPIPE。

回答by neoneye

For completeness sake. On macOS you can use kqueue:

为了完整起见。在 macOS 上,您可以使用 kqueue:

void noteProcDeath(
    CFFileDescriptorRef fdref, 
    CFOptionFlags callBackTypes, 
    void* info) 
{
    // LOG_DEBUG(@"noteProcDeath... ");

    struct kevent kev;
    int fd = CFFileDescriptorGetNativeDescriptor(fdref);
    kevent(fd, NULL, 0, &kev, 1, NULL);
    // take action on death of process here
    unsigned int dead_pid = (unsigned int)kev.ident;

    CFFileDescriptorInvalidate(fdref);
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example

    int our_pid = getpid();
    // when our parent dies we die as well.. 
    LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
    exit(EXIT_SUCCESS);
}


void suicide_if_we_become_a_zombie(int parent_pid) {
    // int parent_pid = getppid();
    // int our_pid = getpid();
    // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);

    int fd = kqueue();
    struct kevent kev;
    EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
    kevent(fd, &kev, 1, NULL, 0, NULL);
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}