python信号,中断系统调用

时间:2012-10-31 17:42:00

标签: python signals

我正在用python编写程序。我希望从stdin读取并处理sigchld。我希望处理任何一个输入,而不是旋转(推测性地为输入采样)。

我无法接听每次通话时被信号中断的系统调用。

我是以错误的方式来做这件事的吗?我可以在没有尝试/除外的情况下使用它吗?

我主要担心的不是尝试/除了我在代码中到目前为止。但是,在程序中的其他代码行中我将需要数百个。它对我来说似乎并不模块化。

以下是一些测试代码:

#!/usr/bin/python

from time import sleep
import select
import signal
import fcntl
import os
import sys

pipe_r, pipe_w = os.pipe()
flags = fcntl.fcntl(pipe_w, fcntl.F_GETFL, 0)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(pipe_w, fcntl.F_SETFL, flags)

signal.signal(signal.SIGCHLD, lambda x,y: None)
signal.signal(signal.SIGALRM, lambda x,y: None)
signal.siginterrupt(signal.SIGCHLD,False) #makes no difference
signal.siginterrupt(signal.SIGALRM,False) #makes no difference
signal.set_wakeup_fd(pipe_w)
signal.setitimer(signal.ITIMER_REAL, 2, 2)

poller = select.epoll()
poller.register(pipe_r, select.EPOLLIN)
poller.register(sys.stdin, select.EPOLLIN)

print "Main screen turn on"
while True:
    events=[]
    try:
        events = poller.poll()
        try:
            for fd, flags in events:
                ch=os.read(fd, 1)
                if fd==pipe_r:
                    sys.stdout.write( "We get Signal" )
                if fd==sys.stdin.fileno():
                    sys.stdout.write( ch )
                sys.stdout.flush()
        except IOError as e:
            print "exception loop" + str(e)
    except IOError as e:
        print "exception poll" + str(e)

版本:

#python --version
python 2.7.3
#uname -a
Linux richard 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

2 个答案:

答案 0 :(得分:6)

是的,你正是以正确的方式解决这个问题。 (好吧,我可能会使用selectpoll而不是epoll,因为那时你的代码可以移植到非Linux平台......)

我假设在这里对两个或更多fds进行轮询对你的设计来说是必然的。如果你只添加epoll循环来处理信号,那么这部分就没有必要;您只需要围绕read调用的简单EINTR循环。但是,一旦你有一个用于其他目的的管道,把它连接起来就像信号唤醒管道是一个合理的事情,然后你的,你所做的一切都隐含在从多个fds读取。

无论哪种方式,你都无法避免try块。 (这不是真的。你可以始终使用sigblock / sigprocmask /等来阻止信号 - 它们不在Python的signal模块中,但是您可以随时ctypes他们。但是,您通常需要更多代码,使用try / finally而不是try / except,以及您的代码变得越来越难以调试,因此除了在处理无法中断且必须被sigblocked的代码的少数情况下,沿着这条路走下去并没有什么意义。)

这是POSIX的标准功能,许多系统调用可以被信号中断,在这种情况下,它们会因EINTR而失败。

大多数特定平台将可以中断的调用列表限制为比POSIX允许的更小的集合,因此您必须查看linux文档以确定究竟是什么和不是EINTR安全的,但是epoll方法系列以及read / recv / write / send和朋友几乎肯定是不安全的。

使用siginterrupt可能有所帮助,但它无法解决问题。特别是,不需要调用来重新启动而不是中断,并且在某些情况下,他们需要中断或完成部分结果而不是重新启动。我很确定selectpoll永远不会重新启动,因此epoll系列很可能也不是,但您必须检查。无论如何,大多数BSD衍生产品默认重启,所以如果linux是相同的,你就不会改变任何东西。

Python本可以用隐式EINTR循环包装所有这些,我相信一些更高级别的方法(如sendall)这样做,但是较低级别的方法不这样做。但你可以自己包装好。例如(在我的头顶,未经测试):

def dopoll(poller):
    while True:
        try:
            return poller.poll()
        except IOError as e:
            if e.errno != EINTR:
                raise

然后,在您呼叫poll的任何地方,请致电dopoll

答案 1 :(得分:1)

我编写了一个python库,它将信号显示为流,因此我可以使用select / poll / epoll等。它使用libc(Gnu C库)来完成艰苦的工作。希望它有用。

https://github.com/richard-delorenzi/pysigstream

如果你想改进它,那么你可以分叉,请发送拉动请求。