Python信号问题:如果在执行另一个信号处理程序时收到SIGQUIT,SIGQUIT处理程序会延迟执行吗?

9 投票
2 回答
4496 浏览
提问于 2025-04-11 09:17

下面这个程序非常简单:它每半秒输出一个点。如果接收到一个SIGQUIT信号,它会输出十个Q。如果接收到一个SIGTSTP信号(也就是按下Ctrl-Z),它会输出十个Z

如果在打印Q的时候接收到SIGTSTP信号,它会在完成十个Q的输出后再打印十个Z。这其实是个好事。

但是,如果在打印Z的时候接收到SIGQUIT信号,它就不会在打印完Z后立即打印Q。相反,只有在我手动终止程序(通过键盘中断)后,它才会打印出Q。我希望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)

2 个回答

1

在Linux 2.6.24上使用Python 2.5.2时,你的代码的运行结果正好和你想要的一样(如果在处理一个信号的时候又收到了另一个信号,新信号会在第一个信号处理完后立即处理)。

而在Linux 2.6.16上使用Python 2.4.4时,我看到了你所描述的问题表现。

我不确定这是否是因为Python或Linux内核的某些变化导致的。

6

你遇到的更大问题是信号处理器中的阻塞。

通常来说,这种做法是不推荐的,因为它可能导致一些奇怪的时间条件。不过,这并不是你问题的根本原因,因为你所面临的时间问题是由于你选择的信号处理器造成的。

无论如何,这里有一种方法可以至少减少这种时间问题,那就是在你的处理器中只设置标志,而把实际的工作留给主循环来完成。关于你的代码为什么表现得奇怪的解释在代码之后。

#!/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的处理器还在运行,它就会把SIGQUIT信号暂时保存起来。等到另一个信号到来,比如你按下CTRL+C(也叫KeyboardInterrupt)时,内核会记得之前没有发送的SIGQUIT信号,并在这时发送它。

你会注意到,如果你把主循环中的while 1:改成for i in range(60):,然后再测试一次,程序会在不运行SIGTSTP处理器的情况下退出,因为退出不会重新触发内核的信号传递机制。

祝你好运!

撰写回答