Python 信号の問題点:別のシグナル ハンドラーの実行中に SIGQUIT を受信した場合、SIGQUIT ハンドラーは実行を遅らせますか?

StackOverflow https://stackoverflow.com/questions/109705

  •  02-07-2019
  •  | 
  •  

質問

次のプログラムは非常に単純です。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 カーネルの変更によるものなのかはわかりません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top