3

io_add_watchPython で (gobject 経由で) モニターを使用する際に問題が発生しました。通知のたびにバッファ全体のノンブロッキング読み取りを実行したいと考えています。コードは次のとおりです (少し短縮されています)。

class SomeApp(object):

   def __init__(self):
      # some other init that does a lot of stderr debug writes
      fl = fcntl.fcntl(0, fcntl.F_GETFL, 0)
      fcntl.fcntl(0, fcntl.F_SETFL, fl | os.O_NONBLOCK)
      print "hooked", gobject.io_add_watch(0, gobject.IO_IN | gobject.IO_PRI, self.got_message, [""])
      self.app = gobject.MainLoop()

   def run(self):
      print "ready"
      self.app.run()

   def got_message(self, fd, condition, data):
      print "reading now"
      data[0] += os.read(0, 1024)
      print "got something", fd, condition, data
      return True

gobject.threads_init()
SomeApp().run()

トリックは次のとおりです。デバッグ出力をアクティブにせずにプログラムを実行すると、got_message呼び出しが行われません。最初に stderr に多くのものを書き込むと、問題はなくなります。このコードに表示される出力以外に何も記述しないと、stdin メッセージ シグナルを取得できません。もう 1 つの興味深い点は、stderr デバッグを有効にして同じアプリを実行しようとするとstrace(見逃した fcntl / ioctl 呼び出しがあるかどうかを確認するために)、問題が再び発生することです。

つまり、strace を使用せずに最初に stderr に大量の書き込みを行うと、io_watch機能します。strace でたくさん書くか、まったく書かないとうまくいきio_watchません。

「some other init」部分には時間がかかるため、「hooked 2」出力が表示される前にテキストを入力し、「ready」後に「ctrl + c」を押すと、get_messageコールバックが呼び出されますが、読み取り呼び出しは EAGAIN をスローします。バッファが空のようです。

標準入力に関連する Strace ログ:

ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
fcntl(0, F_GETFL)                       = 0xa002 (flags O_RDWR|O_ASYNC|O_LARGEFILE)
fcntl(0, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE) = 0
fcntl(0, F_GETFL)                       = 0xa802 (flags O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE)

ここで何が起こっているかについて誰か考えがありますか?


編集:別の手がかり。アプリをリファクタリングして、別のスレッドで読み取りを行い、パイプ経由で戻そうとしました。それは「一種の」作品です:

...
      rpipe, wpipe = os.pipe()
      stopped = threading.Event()
      self.stdreader = threading.Thread(name = "reader", target = self.std_read_loop, args = (wpipe, stopped))
      self.stdreader.start()
      new_data = ""
      print "hooked", gobject.io_add_watch(rpipe, gobject.IO_IN | gobject.IO_PRI, self.got_message, [new_data])

   def std_read_loop(self, wpipe, stop_event):
      while True:
         try:
            new_data = os.read(0, 1024)
            while len(new_data) > 0:
               l = os.write(wpipe, new_data)
               new_data = new_data[l:]
         except OSError, e:
            if stop_event.isSet():
               break
            time.sleep(0.1)
...

同じテキストを新しいパイプに入れるだけで、すべてが機能し始めるのは驚くべきことです。問題はそれです:

  • 最初の行はまったく「認識」されていません-2行目以降の行のみが表示されます
  • それは醜いです

多分それはなぜそれが起こっているのかについて他の誰かの手がかりを与えるでしょうか?

4

3 に答える 3

2

これは、コールバックの設定に多少の遅延があるか、コールバックを設定できるかどうかに影響する環境の変化がある競合状態のように聞こえます。

に電話する前に、何が起こるかを注意深く調べますio_add_watch()。たとえば、Python fcntl ドキュメントには次のように書かれています。

このモジュールのすべての関数は、最初の引数としてファイル記述子 fd を取ります。これは、sys.stdin.fileno() によって返されるような整数ファイル記述子、または本物のファイル記述子を返す fileno() を提供する sys.stdin 自体などのファイル オブジェクトです。

明らかに、STDIN が FD == 0 になると想定するとき、それはあなたがしていることではありません。最初にそれを変更して、もう一度やり直します。

もう1つのことは、FDがすでにブロックされている場合、ブロックされていない他のプロセスが実行されている間、プロセスが待機している可能性があるため、最初に何をするかによってタイミングの違いがあるということです. GTK モジュールをインポートする前であっても、プログラムの開始直後に fcntl をリファクタリングするとどうなりますか?

GTK GUI を使用するプログラムが、そもそも標準入力から読み取りたい理由を理解しているかどうかはわかりません。実際に別のプロセスの出力をキャプチャしようとしている場合は、サブプロセス モジュールを使用してパイプを設定し、パイプで次io_add_watch()のように設定する必要があります。

proc = subprocess.Popen(command, stdout = subprocess.PIPE)
gobject.io_add_watch(proc.stdout, glib.IO_IN, self.write_to_buffer )

この例でも、 を呼び出す前に有効なオープン FD があることを確認しますio_add_watch()。

通常、gobject.io_add_watch()を使用する場合は の直前で呼び出されgobject.MainLoop()ます。たとえば、io_add_watchIO_IN をキャッチするために使用する作業コードを次に示します。

于 2009-10-25T19:27:24.343 に答える
0

ドキュメントには、コールバックから戻る必要があると記載TRUEされています。そうしないと、イベントソースのリストから削除されます。

于 2009-10-19T11:29:38.930 に答える
0

stderr 出力の前に最初にコールバックをフックするとどうなりますか? デバッグ出力を有効にしても呼び出されますか?

os.read()また、呼び出しの間に 1024 バイトを超える準備ができた場合に備えて、データが得られなくなるまでハンドラーを繰り返し呼び出す必要があると思います。

モジュールをバックグラウンド スレッドで使用して機能selectをエミュレートしようとしましたか? gioそれは動作しますか?これはどのプラットフォームで、どのような種類の FD を扱っていますか? (ファイル? ソケット? パイプ?)

于 2009-10-28T17:39:19.757 に答える