Python信号故障:如果在执行另一个信号处理程序期间收到SIGQUIT,则SIGQUIT处理程序会延迟执行吗?

时间:2020-03-06 14:29:43  来源:igfitidea点击:

以下程序非常简单:每半秒钟输出一个点。如果收到SIGQUIT,它将继续输出十个Q。如果接收到SIGTSTP(Ctrl-Z),则输出十个Z。

如果在打印Q时收到SIGTSTP,则在完成十个Q后将打印十个Z。这是一件好事。

但是,如果在打印Z时收到SIGQUIT,则无法在其后打印Q。相反,它仅在我通过KeyboardInterrupt手动终止执行后才将它们打印出来。我希望在Z之后立即打印Q。

这是使用Python2.3发生的。

我究竟做错了什么? Muchas gracias。

#!/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的处理程序仍在运行时,它只是将其保存以备后用。一旦另一个信号通过传递,例如CTRL + C(又名KeyboardInterrupt)时传递给SIGINT,内核就会记住它从未传递过SIGQUIT并立即传递了它。

我们会注意到,如果在主循环中将" while 1:"更改为" for range(60):`中的i"并再次执行测试用例,则程序将退出而不运行SIGTSTP处理程序,因为退出不会重新触发内核的信号传递机制。

祝你好运!

在Linux 2.6.24上的Python 2.5.2上,代码完全按照我们描述的所需结果工作(如果在仍在处理前一个信号的同时接收到信号,则在第一个信号完成后立即处理新信号)。

在Linux 2.6.16上的Python 2.4.4上,我看到了我们描述的问题行为。

我不知道这是由于Python还是Linux内核的变化所致。