当所有权正确时,为什么`killpg`会返回"不允许"?

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

Ste*_*let 6

我也添加了一些调试(略微修改了源代码).当你试图杀死一个已经退出并处于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上.


nne*_*neo 5

你显然无法杀死由僵尸组成的进程组.当一个进程退出时,它会变成一个僵尸,直到有人打电话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.