【问题标题】:python signals, interrupting system callspython信号,中断系统调用
【发布时间】:2012-10-31 17:41:59
【问题描述】:

我正在用 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

【问题讨论】:

    标签: python signals


    【解决方案1】:

    是的,你正在以正确的方式进行。 (好吧,我可能会使用selectpoll 而不是epoll,因为这样您的代码将可以移植到非Linux 平台……)

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

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

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

    大多数特定平台将可以中断的调用列表限制为比 POSIX 允许的更小的集合,因此您必须查看 linux 文档以确切确定哪些是 EINTR 安全的,哪些不是 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

    【讨论】:

    • 我主要担心的不是 try/except 到目前为止的代码。但是在程序中的任何其他代码行中,我都需要数百个。对我来说,它似乎不是模块化的。
    • 这就是你为每个系统调用编写一个包装器的原因。因此,您需要在这六个包装器中的每一个中使用 try/except,但对于数百个调用中的任何一个,您都不需要 try/except。
    • 你是说我需要为每个系统调用或只是一些系统调用的包装器?
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-10
      • 2012-11-01
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      相关资源
      最近更新 更多