9

次のプログラムは非常に単純です。0.5秒ごとに1つのドットを出力します。SIGQUITを受信すると、10個のQを出力します。SIGTSTP Ctrl- Zを受信すると、 10個のZを出力します。

Qの印刷中にSIGTSTPを受信した場合、10個のQを実行した後、 10個のZを印刷します。これは良いことです。

ただし、Zの印刷中にSIGQUITを受信すると、その後のQの印刷に失敗します。代わりに、KeyboardInterruptを介して手動で実行を終了した後にのみそれらを出力します。QをZの直後に印刷したい。

これはPython2.3を使用して発生します。

私は何が間違っているのですか?

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
    for i in range(10):
        write("Q")
        sleep(0.5)

def process_tstp(signum, frame):
    for i in range(10):
        write("Z")
        sleep(0.5)

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    write('.')
    sleep(0.5)
4

2 に答える 2

6

あなたのより大きな問題は、シグナルハンドラでのブロックです。

これは、奇妙なタイミング条件につながる可能性があるため、通常は推奨されません。ただし、シグナル ハンドラーの選択が原因で脆弱なタイミング条件が存在するため、それが問題の原因とは限りません。

とにかく、ハンドラーでフラグを設定するだけで、メインの while ループを離れて実際の作業を行うことで、少なくともタイミング条件を最小限に抑える方法を次に示します。コードの動作がおかしい理由の説明は、コードの後に​​記述されています。

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

print_Qs = 0
print_Zs = 0

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
     global print_Qs
     print_Qs = 10

def process_tstp(signum, frame):
     global print_Zs
     print_Zs = 10

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    if print_Zs:
        print_Zs -= 1
        c = 'Z'
    elif print_Qs:
        print_Qs -= 1
        c = 'Q'
    else:
        c = '.'
    write(c)
    sleep(0.5)

とにかく、ここで何が起こっているのかです。

SIGTSTP は SIGQUIT よりも特別です。

SIGTSTP は、シグナル ハンドラの実行中に他のシグナルが配信されないようにマスクします。カーネルが SIGQUIT を配信し、SIGTSTP のハンドラーがまだ実行中であることを確認すると、後で使用できるように単純に保存します。CTRL+ (別名 KeyboardInterrupt) のときの SIGINT など、別のシグナルが配信Cされると、カーネルは SIGQUIT を配信しなかったことを記憶し、現在配信します。

メイン ループに変更while 1:for i in range(60):てテスト ケースを再度実行すると、プログラムは SIGTSTP ハンドラーを実行せずに終了します。これは、exit がカーネルのシグナル配信メカニズムを再トリガーしないためです。

幸運を!

于 2008-09-20T23:39:49.287 に答える
1

Linux 2.6.24 上の Python 2.5.2 では、コードは目的の結果を記述したとおりに機能します (前のシグナルの処理中にシグナルが受信された場合、最初のシグナルが終了した直後に新しいシグナルが処理されます)。

Linux 2.6.16 上の Python 2.4.4 では、あなたが説明した問題の動作が見られます。

これが Python の変更によるものなのか、Linux カーネルの変更によるものなのかはわかりません。

于 2008-09-20T23:49:18.763 に答える