Python 信号の問題点:別のシグナル ハンドラーの実行中に SIGQUIT を受信した場合、SIGQUIT ハンドラーは実行を遅らせますか?
質問
次のプログラムは非常に単純です。0.5 秒ごとに 1 つのドットを出力します。受信した場合 シグクイット, 、出力 10 に進みます。 Qs.受信した場合 SIGTSTP (Ctrl-Z), 、10を出力します Zs.
受信した場合 SIGTSTP 印刷中 Qs、10 が出力されます Z10のことを終えた後 Qs.これは良いことです。
ただし、 シグクイット 印刷中 Zs、印刷に失敗します Q彼らの後を追っている。代わりに、KeyboardInterrupt を介して実行を手動で終了した後にのみ、それらを出力します。私が欲しいのは Qの直後に印刷されます。 Zs.
これは 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)
解決
より大きな問題は、シグナル ハンドラーでのブロックです。
これは、奇妙なタイミング条件を引き起こす可能性があるため、通常は推奨されません。ただし、シグナル ハンドラーの選択によって脆弱になるタイミング条件が存在するため、それが問題の完全な原因ではありません。
とにかく、ハンドラーにフラグを設定するだけで、メインの 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 のハンドラーがまだ実行中であることを確認すると、単にそれを後で使用するために保存します。配信のために別のシグナルが送信されると、SIGINT などのシグナルが送信されます。 CTRL+C (別名 KeyboardInterrupt)、カーネルは SIGQUIT を配信しなかったことを記憶しており、現在は SIGQUIT を配信しています。
変えれば気づくよ while 1:
に for i in range(60):
メイン ループでテスト ケースを再度実行すると、終了によってカーネルのシグナル配信メカニズムが再トリガーされないため、プログラムは SIGTSTP ハンドラーを実行せずに終了します。
幸運を!
他のヒント
Linux 2.6.24 上の Python 2.5.2 では、コードは意図した結果とまったく同じように動作します (前の信号の処理中に信号を受信した場合、最初の信号が終了した直後に新しい信号が処理されます)。
Linux 2.6.16 上の Python 2.4.4 では、あなたが説明した問題の動作が見られます。
これが Python の変更によるものなのか、Linux カーネルの変更によるものなのかはわかりません。