subprocess 后遗症 - V2EX
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
wikinee
V2EX    Python

subprocess 后遗症

  •  
  •   wikinee 2019-05-24 22:53:00 +08:00 4601 次点击
    这是一个创建于 2347 天前的主题,其中的信息可能已经有所发展或是发生改变。
    用了 subprocess 的 Popen,发现主进程都退出了,子进程连用户注销之后都还能跑…

    有木有办法一块都退出了
    第 1 条附言    2019-05-26 23:58:27 +08:00
    总结一下这两天看的成果:
    方向一:采用钩子函数在进程退出时,发送杀死子进程。
    缺点:经过我尝试,使用 kill -9 这种做法杀死进程,可能信号都来不及发出来。如果是比较安全的环境,可以采取这种做法。(还有个缺点是,可能让某些 GUI 程序不正常,不过也可能是我个人问题)

    方向二:进程组方法
    大概也是上门类似这种钩子函数,不同的是吧子进程和主进程都放到一个进程组里面去。这是高级玩法,不太熟悉。

    方向三:放弃 subprocess,直接 shell 脚本
    反正命令也不多, 直接 cmd & 完事了。。。
    24 条回复    2019-05-30 15:05:30 +08:00
    rogwan
        1
    rogwan  
       2019-05-24 23:30:21 +08:00 via Android
    你要知道进程 id 呀,主进程只负责起,不负责灭,他主进程不知道紫禁城什么时候干完活了啊。要控制的话,要进程间通讯才行
    Ct5T66PVR1bW7b2z
        2
    Ct5T66PVR1bW7b2z  
       2019-05-24 23:31:28 +08:00
    不可能啊,主进程退出了,子进程也会强制退出啊。。
    hakono
        3
    hakono  
       2019-05-24 23:34:50 +08:00
    主进程退出的时候 Popen 调用 kill,( windows 的话 taskkill )手动杀了子进程
    iaozhen
        4
    iyaozhen  
       2019-05-24 23:40:24 +08:00
    一般都是父进程记录子进程的 pid,然后父进程退出时把子进程都 kill 一遍
    Tink
        5
    Tink  
    PRO
       2019-05-25 00:31:24 +08:00 via iPhone
    subprocess 就是有这个问题,Popen 的时候把 pid 返回来保存,结束的时候直接杀掉
    Qzier
        6
    Qzier  
       2019-05-25 00:31:51 +08:00 via iPhone
    proc = subprocess.Popen(...)
    try:
    outs, errs = proc.communicate(timeout=15)
    except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()
    labnotok
        7
    labnotok  
       2019-05-25 00:33:10 +08:00 via Android
    手动 ps aux | grep xxx | awk '{print $2}' | kill 吧,
    我前几天用 multiprocess 的 pool 的时候出了一堆 D 进程,
    D 进程只能手动关闭,父进程 加上 兄弟进程同时关,一般就能杀掉了
    tangrwx
        8
    tangrwx  
       2019-05-25 08:52:00 +08:00 via iPhone
    有个参数的可以开启进程组,好像是 pexec 或者 new_session。,你可以查下文档
    hhhsuan
        9
    hhhsuan  
       2019-05-25 10:54:14 +08:00
    lolizeppelin
        10
    lolizeppelin  
       2019-05-25 18:00:17 +08:00   1
    这些其实都不是 python 的知识
    好好把操作系统信号,和进程 fork 相关看一看

    这些基础知识不熟悉,multiprocess、subprocess 各种问题你都不知道原因,各种标准的处理方法你都不知道
    不熟悉的就不要瞎回答

    比如上面有人说这种完全错误的话

    ~~~
    不可能啊,主进程退出了,子进程也会强制退出啊。。
    ~~


    强调一下!!!这些其实都不是 python 的问题!!
    lolizeppein
        11
    lolizeppelin  
       2019-05-25 18:04:06 +08:00
    上面的直接 kill 都不是正确做法

    如果你子进程没处理信号的话,只能用信号 9,这种做法是非常不标准的
    codehz
        12
    codehz  
       2019-05-25 21:23:41 +08:00
    Linux 平台的话,可以用 prctl PR_SET_PDEATHSIG
    PTLin
        13
    PTLin  
       2019-05-25 22:23:11 +08:00
    你可以用 atexit 注册个 exit handler,然后每次 fork 的时候记录下来,调用 handler 的时候向子进程发送 SIGTERM 信号,子进程也可以注册 signal handler 来进行退出的收尾工作。
    wikinee
        14
    wikinee  
    OP
       2019-05-26 23:46:00 +08:00
    @lolizeppelin 菜鸟研究的不深入,这块实在不太懂。
    @rogwan @hakono @iyaozhen @Tink 保留子进程 PID 的做法确实有效,只有一个致命缺陷,其实类似 -9 那种干掉进程的做法,有些信号什么的根本来挤不发出去,不保险。
    @tangrwx 我看了那个 prexec 什么的参数,说是 fork 之后,启动之前的一个钩子函数,然后文档里直接 NOTE 说了这个参数不保险,
    @Abbeyok 你可以找例子跑跑看,不要在终端 ctrl + C,试试在进程管理器里把主进程停止。
    wikinee
        15
    wikinee  
    OP
       2019-05-26 23:50:31 +08:00
    @codehz @PTLin 这都是高级玩家了,我还没太深入研究这一块。
    我最后用 shell 脚本写了,放弃这种主进程、子进程的做法。
    tangrwx
        16
    tangrwx  
       2019-05-26 23:53:35 +08:00 via iPhone   1
    @wikinee 试试 start_new_session 参数,其实本质是 Linux 进程组和会话的概念。
    wikinee
        17
    wikinee  
    OP
       2019-05-27 00:02:39 +08:00
    @tangrwx 问题下面我补充了,我看到过这种进程组的玩法。我这边命令不多,直接写 shell 脚本了
    hakono
        18
    hakono  
       2019-05-27 01:03:31 +08:00
    @wikinee 你担心主进程被强杀的话,那你可以改变策略在子进程里监视主进程啊,主进程退出的话就退出

    如果子进程的程序不是你写的程序没法改代码加监视逻辑的话…… emmm ……
    解决办法其实有……不过算有点骚操作,你可以往子进程里注入远程代码啊哈哈哈
    在注入的代码里做主进程监视就行了,完美
    lolizeppelin
        19
    lolizeppelin  
       2019-05-27 11:12:55 +08:00
    不需要子进程监控主进程,有个很标准的做法是用管道
    主进程 fork 前开管道

    子进程开一个线程去读管道,后面紧跟强制退出代码 os._exit(1)

    因为这个读取是阻塞的,当主进程挂掉的时候阻塞读能返回空,这样就知道主进程挂了

    规范程序都按标准写法写的, 不懂把 openstack 的 luanch 那部分读透了就都明白了
    PTLin
        20
    PTLin  
       2019-05-27 12:18:30 +08:00
    @wikinee 的确用 shell 实现也是很简单,cmd &放到后台,然后可以用 wait 来等待,不过我又想起来一个方法,进程 fork 后除非显式调用 setpgid 函数更改进程组 id 否则会继承父进程的进程组 id,这样父进程退出前用 kill 函数,pid 参数指定为 0 就可以发送给同一进程组的所有进程了。
    butterfly1211
        21
    butterfly1211  
       2019-05-27 15:48:27 +08:00
    6 楼正解啊
    mythmgn
        22
    mythmgn  
       2019-05-29 10:41:41 +08:00
    楼主可以 wx 搜下我的号 程序员的梦呓指南 看下最近的 python 进程篇, 讲 os.system subprocess.Popen 家族裸用的原罪, 怎么处理子进程\孙子进程的问题
    liuguichao
        24
    liuguichao  
       2019-05-30 15:05:30 +08:00
    pgid = os.getpgid(self.proc.pid)
    os.killpg(pgid, signal.SIGTERM)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     841 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 21:30 PVG 05:30 LAX 14:30 JFK 17:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86