和父母一起杀孩子

2024-04-27 06:14:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个程序生成和通信与CPU重,不稳定的进程,不是我创建的。如果我的应用程序崩溃或被SIGKILL杀死,我希望子进程也被杀死,这样用户就不必跟踪它们并手动杀死它们。

我知道这个话题以前就有过,但我尝试过所有描述的方法,但似乎没有一种方法能经得起考验。

我知道这一定是可能的,因为终端机总是这样。如果我在终端上运行某个东西,然后杀死终端,这些东西总会死掉。

我试过atexit、双叉和ptysatexitsigkill不起作用;double fork根本不起作用;而且ptys我还没有找到使用python的方法。

今天,我发现了prctl(PR_SET_PDEATHSIG, SIGKILL),这应该是当父进程死亡时,子进程命令杀死自己的一种方式。 我试着把它和popen一起使用,但它看起来一点效果都没有:

import ctypes, subprocess
libc = ctypes.CDLL('/lib/libc.so.6')
PR_SET_PDEATHSIG = 1; TERM = 15
implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM)
subprocess.Popen(['gnuchess'], preexec_fn=implant_bomb)

在上面,创建子对象,父对象退出。现在,您可能希望gnuchess收到SIGKILL并死亡,但它没有。我仍然可以在使用100%CPU的进程管理器中找到它。

有人能告诉我我使用prctl有什么问题吗?, 或者你知道终端是如何杀死他们的孩子的吗?


Tags: 方法终端进程prcpuctypeslibcsubprocess
3条回答

事实上,我发现你最初的方法对我来说很好——下面是我测试过的正确的代码示例:

回声.py

#!/bin/env python

import time
import sys
i = 0
try:
    while True:
        i += 1
        print i
        time.sleep(1)
except KeyboardInterrupt:
    print "\nechoer caught KeyboardInterrupt"
    exit(0)

父进程py

#!/bin/env python

import ctypes
import subprocess
import time

libc = ctypes.CDLL('/lib64/libc.so.6')
PR_SET_PDEATHSIG = 1
SIGINT = 2
SIGTERM = 15

def set_death_signal(signal):
    libc.prctl(PR_SET_PDEATHSIG, signal)

def set_death_signal_int():
    set_death_signal(SIGINT)

def set_death_signal_term():
    set_death_signal(SIGTERM)

#subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_term)
subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_int)
time.sleep(1.5)
print "parentProc exiting..."

我知道已经很多年了,但我找到了解决这个问题的一个简单(有点老套)的方法。在父进程中,将所有调用包装在一个非常简单的C程序中,该程序调用prctl(),然后调用exec()来解决Linux上的这个问题。我称之为“叶树”:

#include <linux/prctl.h>
#include <signal.h>
#include <unistd.h>

int main(int argc, char **argv) {
     if(argc < 2)
          return 1;
     prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
     return execvp(argv[1], &argv[1]);
}

当从Python(或任何其他语言)生成子进程时,可以运行“yeshup gnuchess[argments]”。您会发现,当父进程被终止时,应该很好地放弃所有子进程。

这是因为Linux将接受对prctl的调用(不清除它),即使在调用execvp之后(它有效地将yeshup进程“转换”为gnuchess进程,或在那里指定的任何命令),这与fork()不同。

prctlPR_SET_DEATHSIG只能设置为调用prctl的进程,而不能设置为任何其他进程,包括此特定进程的子进程。我所指向的手册页表示这一点的方式是“这个值在fork()上被清除”-fork,当然,这是其他进程(在Linux和任何其他Unix-y操作系统中)的派生方式。

如果您无法控制要在子流程中运行的代码(实际上,对于您的gnuchess示例来说,就是这样的情况),我建议您首先生成一个单独的小“monitor”进程,该进程的作用是跟踪其所有兄弟进程(父进程在生成兄弟进程时可以让monitor知道这些兄弟进程的pid),并在常见父进程死亡时向它们发送致命信号(monitor需要对此进行轮询,在您的选择检查父节点是否仍然存在;使用select在循环中等待来自父节点的更多信息,超时时间为N秒)。

不是微不足道的,但是这样的系统任务通常不是这样的。终端做它的方式不同(通过一个“控制终端”的概念为一个进程组),但当然它是微不足道的任何孩子阻止(双叉,nohup,等等)。

相关问题 更多 >