Dav*_*ver 8 python unix macos bsd
我有一些代码fork()
,setsid()
在孩子中调用,并开始一些处理.如果任何一个孩子退出(waitpid(-1, 0)
),我会杀死所有子进程组:
child_pids = []
for child_func in child_functions:
pid = fork()
if pid == 0:
setsid()
child_func()
exit()
else:
child_pids.append(pid)
waitpid(-1, 0)
for child_pid in child_pids:
try:
killpg(child_pid, SIGTERM)
except OSError as e:
if e.errno != 3: # 3 == no such process
print "Error killing %s: %s" %(child_pid, e)
Run Code Online (Sandbox Code Playgroud)
但是,有时呼叫killpg
将因"不允许操作"而失败:
Error killing 22841: [Errno 1] Operation not permitted
为什么会发生这种情况?
一个完整的,有效的例子:
from signal import SIGTERM from sys import exit from time import sleep from os import * def slow(): fork() sleep(10) def fast(): sleep(1) child_pids = [] for child_func in [fast, slow, slow, fast]: pid = fork() if pid == 0: setsid() child_func() exit(0) else: child_pids.append(pid) waitpid(-1, 0) for child_pid in child_pids: try: killpg(child_pid, SIGTERM) except OSError as e: print "Error killing %s: %s" %(child_pid, e)
产量:
$ python killpg.py Error killing 23293: [Errno 3] No such process Error killing 23296: [Errno 1] Operation not permitted
我也添加了一些调试(略微修改了源代码).当你试图杀死一个已经退出并处于Zombie状态的进程组时,就会发生这种情况.哦,它很容易重复[fast, fast]
.
$ python so.py
spawned pgrp 6035
spawned pgrp 6036
Reaped pid: 6036, status: 0
6035 6034 6035 Z (Python)
6034 521 6034 S+ python so.py
6037 6034 6034 S+ sh -c ps -e -o pid,ppid,pgid,state,command | grep -i python
6039 6037 6034 R+ grep -i python
killing pg 6035
Error killing 6035: [Errno 1] Operation not permitted
6035 6034 6035 Z (Python)
6034 521 6034 S+ python so.py
6040 6034 6034 S+ sh -c ps -e -o pid,ppid,pgid,state,command | grep -i python
6042 6040 6034 S+ grep -i python
killing pg 6036
Error killing 6036: [Errno 3] No such process
Run Code Online (Sandbox Code Playgroud)
不知道如何处理.也许你可以将waitpid置于while循环中以获取所有已终止的子进程,然后继续使用pgkill().
但你的问题的答案是你正在获得EPERM,因为你不允许杀死一个僵尸进程组组长(至少在Mac OS上).
此外,这在python之外是可验证的.如果你在那里睡觉,找到其中一个僵尸的pgrp,并试图杀死它的进程组,你也得到EPERM:
$ kill -TERM -6115
-bash: kill: (-6115) - Operation not permitted
Run Code Online (Sandbox Code Playgroud)
确认这也不会发生在Linux上.
你显然无法杀死由僵尸组成的进程组.当一个进程退出时,它会变成一个僵尸,直到有人打电话waitpid
给它.通常,init
将取得父母已经去世的孩子的所有权,以避免孤儿僵尸儿童.
因此,在某种意义上,该过程仍处于"周围"状态,但它没有CPU时间,并且忽略了kill
直接发送给它的任何命令.但是,如果进程组完全由僵尸组成,则行为似乎是杀死进程组EPERM
而不是静默失败.请注意,终止包含非僵尸的进程组仍然成功.
演示此示例的程序:
import os
import time
res = os.fork()
if res:
time.sleep(0.2)
pgid = os.getpgid(res)
print pgid
while 1:
try:
print os.kill(-pgid, 9)
except Exception, e:
print e
break
print 'wait', os.waitpid(res, 0)
try:
print os.kill(-pgid, 9)
except Exception, e:
print e
else:
os.setpgid(0, 0)
while 1:
pass
Run Code Online (Sandbox Code Playgroud)
输出看起来像
56621
None
[Errno 1] Operation not permitted
wait (56621, 9)
[Errno 3] No such process
Run Code Online (Sandbox Code Playgroud)
父母用SIGKILL杀死孩子,然后再次尝试.第二次,它得到了EPERM
,所以它等待孩子(收获它并摧毁它的进程组).所以,第三个按预期kill
生产ESRCH
.