4

Pythonでプログラムを書いています。stdin から読み込み、sigchld を処理したい。どちらの入力も、回転せずに処理したい (投機的に入力をサンプリングする)。

私が行うすべての呼び出しで、シグナルによって中断されたシステムコールをキャッチできません。

私はこれについて間違った方法で進んでいますか?これをtry/exceptなしで機能させることはできますか?

私の主な心配は、これまでのコードでの 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

はい、あなたはこれについて正しい方法で進んでいます。(ええと、おそらくの代わりにselectorを使用するでしょう。そうすれば、コードは Linux 以外のプラットフォームに移植できるからです…)pollepoll

ここでは、2 つ以上の fds でのポーリングが本質的に設計に必要であると想定しています。シグナルを処理するために epoll ループを追加しただけの場合、その部分は必要ありません。必要なのは、read呼び出しの周りの単純な EINTR ループだけです。しかし、他の目的のためのパイプがあれば、それをシグナル ウェイクアップ パイプとして接続することは妥当なことであり、それからあなたの、そしてあなたがしている他のすべてのことは、複数の fds から読み取ることで暗黙のうちに行われます。

tryいずれにせよ、ブロックを避けることはできません。(これは正確ではありません。シグナルは/ / etcでいつでもブロックできます。シグナルは Python のモジュールにはありませんが、いつでもブロックできます。しかし、通常は、 /の代わりに/を使用して、もう少しコードが必要になります。コードのデバッグが難しくなるため、中断できず、sigblock が必要なコードを扱っている場合を除いて、この道をたどってもあまり意味がありません。)sigblocksigprocmasksignalctypestryfinallytryexcept

多くのシステムコールがシグナルによって中断される可能性があるのは POSIX の標準機能であり、その場合、システムコールは EINTR で失敗します。

ほとんどの特定のプラットフォームでは、中断できる呼び出しのリストが POSIX で許可されているよりも小さいセットに制限されているため、Linux のドキュメントを調べて、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ます。

于 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 に答える