GNU/Linux >> Linux の 問題 >  >> Linux

poll() を使用した名前付きパイプの O_RDWR

open(2) man ページによると、 O_RDONLY|O_NONBLOCK を渡すことができます または O_WRONLY|O_NONBLOCK open を避けるために ブロックされる syscall (errno == ENXIO が返されます) その場合)

私がコメントしたように、fifo(7) と mkfifo(3) の man ページも読んでください。


まず、準備事項:

O_NONBLOCK の使用 および poll() が一般的であり、その逆ではありません。正常に動作するには、すべての poll() を確実に処理する必要があります と read() 状態を正しく返す:

  • read() 0 の戻り値 は EOF を意味します -- 反対側が接続を閉じました。これは (通常、すべての OS ではありませんが) poll() に対応します。 POLLHUP を返す 反省する。 POLLHUP を確認することをお勧めします。 read() を試みる前に 、しかし read() 以来、絶対に必要というわけではありません 0 を返すことが保証されています 書き込み側が閉じた後。
  • read() を呼び出した場合 ライターが接続する前で、O_RDONLY | O_NONBLOCK があります 、EOF(read())を取得します 0 を返す )お気づきのとおり、繰り返し。ただし、poll() を使用すると POLLIN を待つ read() を呼び出す前のイベント 、ライターが接続するのを待ち、EOF を生成しません。
  • read() 戻り値 -1 通常はエラーを意味します。ただし、 errno == EAGAIN の場合 、これは単に現在利用可能なデータがなく、ブロックしていないことを意味するため、poll() に戻ることができます 他のデバイスの取り扱いが必要な場合。 errno == EINTR の場合 、次に read() データを読み取る前に中断されたため、poll() に戻ることができます または単に read() を呼び出します すぐにもう一度。

さて、Linux の場合:

  • 読み込み側で O_RDONLY で開く場合 、次に:
    • open() 対応するライターが開かれるまでブロックします。
    • poll() POLLIN を返します データの読み取り準備が整ったとき、または EOF が発生したときの再試行
    • read() 要求されたバイト数が読み取られるか、接続が閉じられる (0 が返される) か、シグナルによって中断されるか、致命的な IO エラーが発生するまでブロックされます。このようなブロッキングは、poll() を使用する目的を無効にします。 、これが poll() の理由です ほとんどの場合 O_NONBLOCK で使用されます . alarm() を使用できます read() から目覚める タイムアウト後ですが、それは複雑すぎます。
    • ライターが閉じると、リーダーは poll() を受け取ります POLLHUP revent と read() 0 を返します 以降無期限。この時点で、リーダーはファイルハンドルを閉じてから再度開く必要があります。
  • 読み込み側で O_RDONLY | O_NONBLOCK で開いた場合 、次に:
    • open() ブロックしません。
    • poll() POLLIN を返します データを読み取る準備ができたとき、または EOF が発生したときは、revent します。 poll() ライターが存在しない場合は、ライターが使用可能になるまでブロックします。
    • 現在利用可能なすべてのデータが読み取られた後、read() -1 を返し、errno == EAGAIN を設定します。 接続がまだ開いている場合、または 0 が返されます 接続が閉じている (EOF) またはライターによってまだ開かれていない場合 . errno == EAGAINの場合 、これは poll() に戻る時が来たことを意味します 、接続は開いていますが、それ以上データがないためです。 errno == EINTRの場合 、 read() まだバイトを読み取っておらず、シグナルによって中断されたため、再起動できます。
    • ライターが閉じると、リーダーは poll() を受け取ります POLLHUP 反省し、read() 0 を返します 以降無期限。この時点で、リーダーはファイルハンドルを閉じてから再度開く必要があります。
  • (Linux 固有:) O_RDWR で読み取り側で開いた場合 、次に:
    • open() ブロックしません。
    • poll() POLLIN を返します データを読み取る準備ができたら、revent します。ただし、名前付きパイプの場合、EOF は POLLIN を引き起こしません。 または POLLHUP
    • read() 要求されたバイト数が読み取られるか、シグナルによって中断されるか、その他の致命的な IO エラーが発生するまでブロックされます。名前付きパイプの場合、errno == EAGAIN は返されません 、また 0 を返すことさえありません の一つ。要求された正確なバイト数が読み取られるまで、またはシグナルを受信するまで、そのまま待機します (この場合、これまでに読み取られたバイト数を返すか、-1 を返して errno == EINTR を設定します)。 これまでにバイトが読み取られなかった場合)。
    • ライターが閉じた場合、後で別のライターが名前付きパイプを開いても、リーダーは名前付きパイプを読み取ることができなくなりますが、リーダーは通知を受け取りません。
  • (Linux 固有:) O_RDWR | O_NONBLOCK で読み取り側で開いた場合 、次に:
    • open() ブロックしません。
    • poll() POLLIN を返します データを読み取る準備ができたら、revent します。ただし、EOF は POLLIN を引き起こしません。 または POLLHUP 名前付きパイプのrevents.
    • 現在利用可能なすべてのデータが読み取られた後、read() -1 を返します errno == EAGAIN を設定します .今こそ poll() に戻る時です おそらく他のストリームから、さらにデータを待ちます。
    • ライターが閉じても、後で別のライターが名前付きパイプを開いても、リーダーは名前付きパイプを読み取ることができなくなります。接続は永続的です。

ご指摘のとおり、 O_RDWR を使用して with pipes は POSIX などの標準ではありません。

ただし、この質問は頻繁に出てくるようなので、Linux で、片側が閉じても生き続け、POLLHUP を引き起こさない「回復力のある名前付きパイプ」を作成する最善の方法は、 反論または 0 を返します read() の場合 、 O_RDWR | O_NONBLOCK を使用することです .

Linux で名前付きパイプを処理する主な方法は 3 つあります。

<オール>
  • (ポータブル。) poll() なし 、および単一のパイプを使用:

    • open(pipe, O_RDONLY);
    • メインループ:
      • read() read() でループする可能性がある、必要なだけのデータ 呼び出します。
        • If read() == -1errno == EINTRread() もう一度。
        • If read() == 0 、接続が閉じられ、すべてのデータが受信されました。

  • (ポータブル。) poll() で 、そして、名前付きパイプであっても、パイプは一度しか開かれず、一度閉じられると、新しいパイプラインを設定して、リーダーとライターの両方によって再度開かれる必要があることを期待して:

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • メインループ:
      • poll() POLLIN の場合 イベント、おそらく一度に複数のパイプで。 (注:これにより、read() が防止されます ライターが接続する前に複数の EOF を取得しないようにします)
      • read() 必要なだけのデータ、おそらく read() でループ 呼び出します。
        • If read() == -1 そして errno == EAGAINpoll()に戻ります ステップ。
        • If read() == -1 および errno == EINTRread() もう一度。
        • If read() == 0 、接続が閉じられているため、パイプを終了するか、閉じてから再度開く必要があります。

  • (非移植性、Linux 固有。) poll() を使用 、および名前付きパイプが終了することはなく、複数回接続および切断される可能性があることを期待して:

    • open(pipe, O_RDWR | O_NONBLOCK);
    • メインループ:
      • poll() POLLIN の場合 イベント、おそらく一度に複数のパイプで。
      • read() 必要なだけのデータ、おそらく read() でループ 呼び出します。
        • If read() == -1errno == EAGAINpoll()に戻ります ステップ。
        • If read() == -1errno == EINTRread() もう一度。
        • If read() == 0 、何かが間違っています -- O_RDWR では発生しないはずです 名前付きパイプで、ただし O_RDONLY のみ または名前のないパイプ。閉じて再度開く必要がある閉じたパイプを示します。名前付きパイプと名前なしパイプを同じ poll() に混在させた場合 イベント処理ループ、このケースはまだ処理する必要があるかもしれません.

  • Linux
    1. Bashを使用してファイルから行を読み取る:対その間?

    2. poll() はタイムアウト 0 で何をしますか?

    3. 読み取りエラーのある障害のあるブロック デバイスをシミュレートしますか?

    1. Linux:タイムアウトのあるソケットからの読み取りまたは受信はありますか?

    2. Linux Bash での名前付きパイプの使用例

    3. poll が epoll に置き換えられないのはなぜですか?

    1. Linuxでパイプと名前付きパイプを使用する方法(例付き)

    2. Linuxコマンドラインでのパイプの操作

    3. Catでディスクをクローンする際のエラー?