4

我正在用python编写一个程序。我希望从标准输入读取,并处理 sigchld。我想在输入时处理任何一个输入,而不需要旋转(推测性地对输入进行采样)。

我无法在每次通话时捕捉到被信号中断的系统调用。

我会以错误的方式解决这个问题吗?我可以在不尝试/例外的情况下让它工作吗?

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

这是一些测试代码:

#!/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
4

2 回答 2

6

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

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

无论哪种方式,你真的无法避免try障碍。(这并不完全正确。您始终可以sigblock使用/等阻止信号。sigprocmask它们不在 Python 的signal模块中,但您始终可以使用ctypes它们。但是您通常需要稍微多一点的代码,使用try/finally而不是try/ except,并且您的代码变得更难以调试,因此沿着这条路走下去没有多大意义,除非在少数情况下您正在处理无法中断且必须被 sigblocked 的代码。)

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

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

照常使用siginterrupt可能会有所帮助,但不能解决问题。特别是,不需要调用来重新启动而不是中断,并且在某些情况下,它们需要中断或完成部分结果而不是重新启动。我很确定select并且poll永远不会重新启动,所以很有可能这个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

于 2012-10-31T17:59:08.207 回答
1

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

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

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

于 2014-03-04T12:23:03.933 回答