9

下面的程序非常简单:它每半秒输出一个点。如果它收到一个SIGQUIT,它会继续输出 10 个Q s。如果它收到一个SIGTSTP ( Ctrl- Z),它会输出 10 个Z

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

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

这发生在 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+时的 SIGINT C(又名 KeyboardInterrupt),内核会记住它从未传递过 SIGQUIT 并现在传递它。

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

祝你好运!

于 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 回答