8

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)

ただし、killpg「操作が許可されていません」で呼び出しが失敗することがあります。

22841 を殺すエラー: [Errno 1] 操作は許可されていません

なぜこれが起こっているのでしょうか?

完全な実際の例:

シグナルインポート SIGTERM から
sysインポート出口から
from time import sleep
OS インポートから *

デフスロー():
    フォーク()
    スリープ(10)

デフォルト高速():
    スリープ(1)

child_pids = []
[高速、低速、低速、高速] の child_func の場合:
    pid = フォーク()
    pid == 0 の場合:
        セットid()
        child_func()
        終了(0)
    そうしないと:
        child_pids.append(pid)

ウェイトピッド(-1, 0)
child_pids の child_pid の場合:
    試す:
        killpg(child_pid, SIGTERM)
    e: として OSError を除く
        print "%s の強制終了エラー: %s" %(child_pid, e)

どちらが得られますか:

$ python killpg.py
23293 を殺すエラー: [Errno 3] そのようなプロセスはありません
23296 を殺すエラー: [Errno 1] 操作は許可されていません
4

3 に答える 3

6

デバッグも追加しました(ソースを少し変更しました)。すでに終了し、ゾンビ状態になっているプロセス グループを強制終了しようとすると発生します。ああ、それは だけで簡単に再現でき[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

それに対処する方法がわからない。おそらく、waitpid を while ループに入れて、終了したすべての子プロセスを取得し、残りを pgkill() することができます。

しかし、あなたの質問に対する答えは、(少なくとも Mac OS では) ゾンビ プロセス グループ リーダーを killpg することは許可されていないため、EPERM を取得しているということです。

また、これは Python の外部で検証可能です。そこにスリープを入れて、それらのゾンビの 1 つの pgrp を見つけ、そのプロセス グループを強制終了しようとすると、EPERM も取得されます。

$ kill -TERM -6115
-bash: kill: (-6115) - Operation not permitted

これが Linux でも発生しないことを確認しました。

于 2012-09-20T23:51:50.800 に答える
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

出力は次のようになります

56621
None
[Errno 1] Operation not permitted
wait (56621, 9)
[Errno 3] No such process

親は SIGKILL で子を殺してから、再試行します。2 回目は を取得EPERMするので、子プロセスを待ちます (子プロセスをリープし、そのプロセス グループを破棄します)。したがって、3 番目は期待どおりにkill生成ESRCHされます。

于 2012-09-21T00:13:04.547 に答える
1

ログを追加すると、killpg が ESRCH ではなく EPERM を返すことがあるようです。

#!/usr/bin/python

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()
        print child_func, getpid(), getuid(), geteuid()
        child_func()
        exit(0)
    else:
        child_pids.append(pid)

print waitpid(-1, 0)
for child_pid in child_pids:
    try:
        print child_pid, getpgid(child_pid)
    except OSError as e:
        print "Error getpgid %s: %s" %(child_pid, e)      
    try:
        killpg(child_pid, SIGTERM)
    except OSError as e:
        print "Error killing %s: %s" %(child_pid, e)

killpg が EPERM で失敗するたびに、getpgid は以前に ESRCH で失敗しました。例えば:

<function fast at 0x109950d70> 26561 503 503
<function slow at 0x109950a28> 26562 503 503
<function slow at 0x109950a28> 26563 503 503
<function fast at 0x109950d70> 26564 503 503
(26564, 0)
26561 Error getpgid 26561: [Errno 3] No such process
Error killing 26561: [Errno 1] Operation not permitted
26562 26562
26563 26563
26564 Error getpgid 26564: [Errno 3] No such process
Error killing 26564: [Errno 3] No such process

なぜこれが起こるのか、私にはわかりません— それが合法的な振る舞いなのか、Darwin のバグ (FreeBSD などから継承されたもの) なのかなどです。

を呼び出して EPERM を再確認することで、このように回避できるようですkill(child_pid, 0)。それが ESRCH を返す場合、実際の権限の問題はありません。もちろん、これはコードではかなり見苦しく見えます:

for child_pid in child_pids:
    try:
        killpg(child_pid, SIGTERM)
    except OSError as e:
        if e.errno != 3: # 3 == no such process
            if e.errno == 1:
                try:
                    kill(child_pid, 0)
                except OSError as e2:
                    if e2.errno != 3:
                        print "Error killing %s: %s" %(child_pid, e)
            else:
                print "Error killing %s: %s" %(child_pid, e)
于 2012-09-20T23:34:34.680 に答える