Python 如何一次性终止某个子进程下面的所有子进程?

2020-12-29 18:29:43 +08:00
 zictos
比如主进程 m 创建了子进程 p
子进程 p 又创建了子进程 p1 、p2 、p3 、p4,甚至 p1 下面又有创建别的子进程。

在主进程 m 处用 p.kill()或者 p.terminate()可以终止子进程 p,但是 p 创建的子进程 p1,p2,p3,p4 还会继续保持运行。

希望一次性把子进程 p 以及子进程 p 下面的子进程全部结束,并且主进程 m 继续保持运行。有办法吗?


ps:■■■■■■■■■■■■■■■■■■
官方帮助中有说到关于 terminate()的注意事项:进程的后代进程将不会被终止 —— 它们将简单地变成孤立的。

这样的话感觉 kill()或 terminate()有点坑,比如你的某个子进程使用到了某个第三方模块,该模块里面也用到了多进程,而你如果直接 kill 掉该子进程,但子进程下面创建的子进程却并不能 kill 掉,而且可能不容易察觉到,毕竟第三方模块不是自己写的。
4314 次点击
所在节点    Python
27 条回复
black11black
2020-12-29 18:41:48 +08:00
提一个思路,开一个子线程运行 terminal,通过 ps 命令获取完整进程树,Linux 中应该不算繁重操作,效率可以。
emSaVya
2020-12-29 18:45:54 +08:00
进程组
codehz
2020-12-29 18:54:11 +08:00
得区分系统,windows 10 下大概可以使用 job 对象配合 silo 隔离
linux 可以用 pid namespace 隔离
zictos
2020-12-29 19:07:21 +08:00
@black11black #1
@emSaVya #2
能识别是哪个子进程下面的子进程吗?因为可能有多个进程,并且不同进程下面都有子进程,而我只需要结束某个子进程以及该子进程下面的子进程。

python 我只知道能直接获取当前进程下面的所有子进程的 pid (子进程下面的子进程好像不行),对于我题的例子来说,能不能直接在主进程 m 处获取子进程 p 下面的所有子进程的 pid 列表呢?然后再分别终止对应 pid 。
或者如果事先对每个子进程进行有规则的命名,有没有办法通过名称来达到直接终止进程的目的呢?
zictos
2020-12-29 19:14:08 +08:00
笨办法不是没有,比如每创建一个子进程就把子进程的 pid 以及归属哪个父进程的信息写入全局变量中或写入数据库或者写入文本中,然后结束时一个个判断 pid 属于哪个分组并且判断是否存在就行了。只是不知道有没有更简单的,可能有,但是没发现。另外如果是第三方模块创建的子进程这么做就不行了
tonic
2020-12-29 19:30:27 +08:00
https://github.com/douban/CaoE

嘻嘻嘻, 教授封装过
fish267
2020-12-29 19:33:41 +08:00
1. 子线程,带个是否退出标志,自己轮询
2. thread.join()
Jirajine
2020-12-29 19:34:04 +08:00
psutil
tonic
2020-12-29 19:36:58 +08:00
似乎跟你场景有点差别, 不过可以参考下自己封装个 =.=
codehz
2020-12-29 19:56:57 +08:00
跟踪进程的方法有竞争条件,而且还有可能故意逃逸(比如 fork 两次 setsid 就出去了(
思考下 android 的停止运行功能是怎么被绕过的(
lsc
2020-12-29 20:06:36 +08:00
试试执行 shell,kill -9 pstree ?
black11black
2020-12-29 20:15:06 +08:00
@zictos 参考命令`ps ax -o pid,ppid,cmd`,可以直接构建出树,唯一瑕疵是这是请求全系统的树,浪费了资源。
yucongo
2020-12-29 23:42:42 +08:00
import psutil

parent_pid = 30437
parent = psutil.Process(parent_pid)
for child in parent.children(recursive=True): # or parent.children() for recursive=False
.... child.kill()
parent.kill()

SO 答案
zictos
2020-12-29 23:43:41 +08:00
@tonic #6
其实把子进程设为守护进程就可以让子进程在父进程被杀死时自动退出,但守护进程下面不能再创建子进程了。
p = Process(target=test) #创建子进程 p
p.daemon = True #把子进程 p 设为守护进程
p.start()
zictos
2020-12-30 00:43:45 +08:00
@yucongo #13
可用,不过你的代码好像根本描述的效果不一样,我刚自己写着尝试了一下。把下面代码中的“[四空格]”替换成真正的 4 个空格就可以直接运行了
■■■■■■■■分割■■■■■■■■■■



from multiprocessing import Process
import psutil, os, time

def test1():
[四空格]print('我是主线程创建的第一个子线程,我的 pid 是%s' % os.getpid())
[四空格]#在第一个子线程下面继续创建子线程
[四空格]p2 = Process(target=test2)
[四空格]p2.start()
[四空格]time.sleep(1000)

def test2():
[四空格]print('我是子线程创建的子线程,是主线程的孙线程,我的 pid 是%s' % os.getpid())
[四空格]#在第二个子线程下面继续创建子线程
[四空格]p3 = Process(target=test3)
[四空格]p3.start()
[四空格]time.sleep(1000)

def test3():
[四空格]print('我是主线程的孙线程创建的子线程,我的 pid 是%s' % os.getpid())
[四空格]time.sleep(1000)

if __name__ == '__main__':
[四空格]#创建子线程 p1
[四空格]p1 = Process(target=test1)
[四空格]p1.start()

[四空格]#获取子线程 p1 的 pid
[四空格]pid = p1.pid

[四空格]#等待 4 秒后获取子线程 p1 下面所有子线程的 pid
[四空格]time.sleep(4)
[四空格]pidlist = psutil.Process(pid).children(recursive=True)
[四空格]print('pid 为%s 下面的所有子线程为%s' % (pid, pidlist))
zictos
2020-12-30 00:47:08 +08:00
15 楼写错了,“进程”二字全部写成了“线程”
abersheeran
2020-12-30 09:39:17 +08:00
这个事可以是各个进程主动检测自己的父进程是否存活,也可以是各个拥有子进程的父进程在退出前杀死自己所有的子进程。
JCZ2MkKb5S8ZX9pq
2020-12-30 09:46:24 +08:00
有类似需求,现在用的就是记 pid 的笨办法,蹲一个。
fuse
2020-12-30 11:59:14 +08:00
cgroup
鼎鼎大名 systemd 实现方式不知道
zictos
2020-12-30 18:02:06 +08:00
@abersheeran #17
@JCZ2MkKb5S8ZX9pq #18

15 楼已经解决了,已经可以获取到子进程对象了,杀进程可以再加一个循环
for i in pidlist:
####i.kill()

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/740071

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX