V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zictos
V2EX  ›  Python

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

  •  
  •   zictos · 2020-12-29 18:29:43 +08:00 · 4311 次点击
    这是一个创建于 1454 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如主进程 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 掉,而且可能不容易察觉到,毕竟第三方模块不是自己写的。
    27 条回复    2021-01-11 22:22:16 +08:00
    black11black
        1
    black11black  
       2020-12-29 18:41:48 +08:00
    提一个思路,开一个子线程运行 terminal,通过 ps 命令获取完整进程树,Linux 中应该不算繁重操作,效率可以。
    emSaVya
        2
    emSaVya  
       2020-12-29 18:45:54 +08:00
    进程组
    codehz
        3
    codehz  
       2020-12-29 18:54:11 +08:00   ❤️ 1
    得区分系统,windows 10 下大概可以使用 job 对象配合 silo 隔离
    linux 可以用 pid namespace 隔离
    zictos
        4
    zictos  
    OP
       2020-12-29 19:07:21 +08:00
    @black11black #1
    @emSaVya #2
    能识别是哪个子进程下面的子进程吗?因为可能有多个进程,并且不同进程下面都有子进程,而我只需要结束某个子进程以及该子进程下面的子进程。

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

    嘻嘻嘻, 教授封装过
    fish267
        7
    fish267  
       2020-12-29 19:33:41 +08:00
    1. 子线程,带个是否退出标志,自己轮询
    2. thread.join()
    Jirajine
        8
    Jirajine  
       2020-12-29 19:34:04 +08:00
    psutil
    tonic
        9
    tonic  
       2020-12-29 19:36:58 +08:00
    似乎跟你场景有点差别, 不过可以参考下自己封装个 =.=
    codehz
        10
    codehz  
       2020-12-29 19:56:57 +08:00   ❤️ 1
    跟踪进程的方法有竞争条件,而且还有可能故意逃逸(比如 fork 两次 setsid 就出去了(
    思考下 android 的停止运行功能是怎么被绕过的(
    lsc
        11
    lsc  
       2020-12-29 20:06:36 +08:00
    试试执行 shell,kill -9 pstree ?
    black11black
        12
    black11black  
       2020-12-29 20:15:06 +08:00
    @zictos 参考命令`ps ax -o pid,ppid,cmd`,可以直接构建出树,唯一瑕疵是这是请求全系统的树,浪费了资源。
    yucongo
        13
    yucongo  
       2020-12-29 23:42:42 +08:00 via Android   ❤️ 1
    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
        14
    zictos  
    OP
       2020-12-29 23:43:41 +08:00
    @tonic #6
    其实把子进程设为守护进程就可以让子进程在父进程被杀死时自动退出,但守护进程下面不能再创建子进程了。
    p = Process(target=test) #创建子进程 p
    p.daemon = True #把子进程 p 设为守护进程
    p.start()
    zictos
        15
    zictos  
    OP
       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
        16
    zictos  
    OP
       2020-12-30 00:47:08 +08:00
    15 楼写错了,“进程”二字全部写成了“线程”
    abersheeran
        17
    abersheeran  
       2020-12-30 09:39:17 +08:00
    这个事可以是各个进程主动检测自己的父进程是否存活,也可以是各个拥有子进程的父进程在退出前杀死自己所有的子进程。
    JCZ2MkKb5S8ZX9pq
        18
    JCZ2MkKb5S8ZX9pq  
       2020-12-30 09:46:24 +08:00
    有类似需求,现在用的就是记 pid 的笨办法,蹲一个。
    fuse
        19
    fuse  
       2020-12-30 11:59:14 +08:00 via iPhone
    cgroup
    鼎鼎大名 systemd 实现方式不知道
    zictos
        20
    zictos  
    OP
       2020-12-30 18:02:06 +08:00
    @abersheeran #17
    @JCZ2MkKb5S8ZX9pq #18

    15 楼已经解决了,已经可以获取到子进程对象了,杀进程可以再加一个循环
    for i in pidlist:
    ####i.kill()
    zictos
        21
    zictos  
    OP
       2020-12-30 18:44:46 +08:00
    写成函数,只要传入某个进程的 pid 就可以杀死该进程以及该进程下面的子进程。把 killprocess(pid)中的 pid 替换为要杀的进程的 pid 即可

    def killprocess(pid):
    ****import psutil
    ****childlist = psutil.Process(pid).children(recursive=True)
    ****for i in childlist:
    ********i.kill()
    ****psutil.Process(pid).kill()
    abersheeran
        22
    abersheeran  
       2020-12-30 20:01:45 +08:00
    @zictos 学到了。 原来还可以这么获取全部下属进程。
    julyclyde
        23
    julyclyde  
       2020-12-31 16:28:34 +08:00
    @zictos 你这个似乎只能 kill 一层?
    正确的做法应该是深度优先遍历然后再从最深层往上 kill 吧
    zictos
        24
    zictos  
    OP
       2020-12-31 16:35:16 +08:00
    @julyclyde #23
    针对 21 楼的代码,childlist 就已经包含了所有子进程对象了,直接用 for 遍历再逐个 kill 自然就是把所有子进程都杀死了。
    最后一行 psutil.Process(pid).kill()是杀死 pid 为传入的 pid 的进程,前面的 for 是杀死 pid 下面的子进程、孙进程……
    julyclyde
        25
    julyclyde  
       2020-12-31 16:48:16 +08:00
    @zictos 哦。看到 recursive 了。不过顺序正确吗?
    zictos
        26
    zictos  
    OP
       2020-12-31 17:04:08 +08:00
    @julyclyde #25 我没研究过顺序,你可以自己测试下。反正我问题中的需求就是希望能获取某个进程下所有的子进程以及孙进程等等等等,然后杀死他们。所以顺序根本就不重要。
    codehz
        27
    codehz  
       2021-01-11 22:22:16 +08:00
    @zictos #24 考虑竞争条件,你杀进程的时候它也可以同时 fork 出一个新的(不一定是对抗,可能只是巧合,比如多进程编译的时候就不能这样做)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1600 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 16:33 · PVG 00:33 · LAX 08:33 · JFK 11:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.