是的,您正在按照正确的方式进行操作。(好吧,我可能会使用select
或poll
代替epoll
,因为那样您的代码将可以移植到非Linux平台上……)
我在这里假设对两个或多个fds进行轮询本质上是您设计的必要条件。如果您仅添加了epoll循环来处理信号,则不需要该部分。您所需要的只是围绕该read调用的简单EINTR循环。但是,一旦有了用于其他目的的管道,将其作为信号唤醒管道进行连接是一件很合理的事情,那么,您以及您正在执行的所有其他操作都隐含了从多个fds中读取的内容。
无论哪种方式,您实际上都无法避免try障碍。(事实并非如此。您始终可以使用sigblock/ / sigprocmasketc阻止信号。它们不在Python的signal模块中,但您可以始终使用ctypes它们。但是通常您需要使用try/finally而不是try/的更多代码except。并且您的代码变得更加难以调试,因此沿着这条路没有什么意义,除非在少数情况下,您正在处理无法被中断且必须被签名的代码。)
POSIX的标准功能是许多系统调用可以被信号中断,在这种情况下,它们会因EINTR而失败。
大多数特定的平台都将可中断的呼叫列表限制为比POSIX允许的更小的呼叫列表,因此,您必须查看linux文档,以确保确切的是EINTR安全和不安全的EINTR安全,但是epoll方法家族,read/ recv/ write/send
和朋友,几乎可以肯定是不安全的。
照常使用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
哪里,都可以打电话。