web-dev-qa-db-ja.com

Linuxソケットを処理する方法は、POLLERR、POLLHUP、およびPOLLNVALを回避しますか?

投票でこれらのビットを設定するときに何をすべきか疑問に思っていますか?ソケットを閉じて、無視しますか?

15
user3790882

POLLHUPは、ソケットが接続されていないことを意味します。 TCPでは、これはFINが受信および送信されたことを意味します。

POLLERRは、ソケットが非同期エラーを受け取ったことを意味します。 TCPでは、これは通常、RSTが受信または送信されたことを意味します。ファイル記述子がソケットでない場合、POLLERRは、デバイスがポーリングをサポートしていないことを意味する場合があります。

上記の両方の条件で、ソケットファイル記述子はまだ開いており、まだ閉じられていません(ただし、shutdown()はすでに呼び出されている可能性があります)。ファイル記述子のclose()は、ソケットに代わってまだ予約されているリソースを解放します。理論的には、ソケットをすぐに再利用できるはずです(たとえば、別のconnect()呼び出しで)。

POLLNVALは、ソケットファイル記述子が開いていないことを意味します。 close()するのはエラーです。

15
jxh

それは正確なエラーの性質に依存します。問題を確認するには、getsockopt()を使用します。

int error = 0;
socklen_t errlen = sizeof(error);
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);

値: http://www.xinotes.net/notes/note/1793/

最も簡単な方法は、どのような場合でもソケットが使用できなくなったと想定して、ソケットを閉じることです。

9
hasan

POLLNVALは、ファイル記述子の値が無効であることを意味します。これは通常、プログラムのエラーを示しますが、ファイル記述子を閉じていて、それ以降にファイルを開いていない場合は、pollPOLLNVALを返すことに頼ることができます。ディスクリプタ。

POLLERRは、 select のエラーイベントに似ています。これは、readまたはwrite呼び出しがエラー状態(I/Oエラーなど)を返すことを示します。これには、selecterrorfdsマスクを介して信号を送る帯域外データは含まれませんが、pollPOLLPRIを介して信号を送ります。

POLLHUPは基本的に、接続のもう一方の端にあるものが接続の端を閉じたことを意味します。 [〜#〜] posix [〜#〜] はそれを次のように説明します

デバイスが切断されました。このイベントとPOLLOUTは相互に排他的です。ハングアップが発生した場合、ストリームを書き込み可能にすることはできません。

これは端末にとって十分に明らかです:端末が消えました(SIGHUPを生成する同じイベント:モデムセッションが終了した、端末エミュレータウィンドウが閉じられたなど)。 POLLHUPが通常のファイルに送信されることはありません。パイプとソケットの場合、 オペレーティングシステムによって異なります 。 Linuxは、パイプの書き込み側のプログラムがパイプを閉じるときにPOLLHUPを設定し、ソケットのもう一方の端がソケットを閉じるときにPOLLIN|POLLHUPを設定しますが、POLLINはソケットのシャットダウン。最近の* BSDは、パイプの書き込み側がパイプを閉じるときにPOLLIN|POLLUPを設定し、ソケットの動作はより可変的です。

2
Gilles

最小FIFO例

これらの状態がいつ発生するかを理解すれば、それらをどうするかを簡単に知ることができます。

poll.c

_#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(void) {
    char buf[1024];
    int fd, n;
    short revents;
    struct pollfd pfd;

    fd = open("poll0.tmp", O_RDONLY | O_NONBLOCK);
    pfd.fd = fd;
    pfd.events = POLLIN;
    while (1) {
        puts("loop");
        poll(&pfd, 1, -1);
        revents = pfd.revents;
        if (revents & POLLIN) {
            n = read(pfd.fd, buf, sizeof(buf));
            printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
        }
        if (revents & POLLHUP) {
            printf("POLLHUP\n");
            close(pfd.fd);
            pfd.fd *= -1;
        }
        if (revents & POLLNVAL) {
            printf("POLLNVAL\n");
        }
        if (revents & POLLERR) {
            printf("POLLERR\n");
        }
    }
}
_

GitHubアップストリーム

コンパイル:

_gcc -o poll.out -std=c99 poll.c
_

使用法:

_Sudo mknod -m 666 poll0.tmp p
./poll.out
_

別のシェルで:

_printf a >poll0.tmp
_

[〜#〜] pollhup [〜#〜]

ソースを変更しない場合:_./poll.out_出力:

_loop
POLLIN n=1 buf=a
loop
POLLHUP
loop
_

そう:

  • POLLINは、入力が利用可能になると発生します
  • POLLHUPは、ファイルがprintfによって閉じられたときに発生します
  • close(pfd.fd);と_pfd.fd *= -1;_がクリーンアップし、POLLHUPの受信を停止します
  • pollが永久にハングします

これは通常の操作です。

これで、FIFOで次のopenを待つか、完了したらループを終了することができます。

[〜#〜] pollnal [〜#〜]

コメントアウトした場合_pfd.fd *= -1;_:_./poll.out_出力:

_POLLIN n=1 buf=a
loop
POLLHUP
loop
POLLNVAL
loop
POLLNVAL
...
_

そして永遠にループします。

そう:

  • POLLINPOLLHUPcloseは以前と同じように発生しました
  • _pfd.fd_を負の数に設定しなかったため、pollは閉じたfdを使用しようとし続けます
  • これはPOLLNVALを永久に返し続けます

したがって、これは発生してはならないことであり、コードにバグがあることを示しています。

[〜#〜] pollerr [〜#〜]

FIFOでPOLLERRを生成する方法がわかりません。方法があれば教えてください。しかし、デバイスドライバーの_file_operations_で可能になるはずです。

Ubuntu 14.04でテスト済み。