ソケット記述子で select() を使用する場合、このソケットは事前に NONBLOCKING に設定する必要があるという記憶があります。
しかし今日、ソケットをNON-BLOCKINGに設定する行がないように見えるソースファイルを読みました 私の記憶は正しいですか?
ありがとう!
ソケット記述子で select() を使用する場合、このソケットは事前に NONBLOCKING に設定する必要があるという記憶があります。
しかし今日、ソケットをNON-BLOCKINGに設定する行がないように見えるソースファイルを読みました 私の記憶は正しいですか?
ありがとう!
彼が言うとき、ダスクワフは正しい考えを持っています
通常、ソケットを select() で使用するために非ブロッキングとして設定する必要はありません。
これは、カーネルが select() に関して POSIX に準拠している場合に当てはまります。残念ながら、Linux の select() のマニュアル ページにあるように、Linux を使用している人もいますが、そうではありません。
Linux では、select() がソケット ファイル記述子を「読み取り準備完了」と報告する場合がありますが、その後の読み取りはブロックされます。これは、たとえば、データが到着したが、検査時に間違ったチェックサムがあり、破棄された場合に発生する可能性があります。ファイル記述子が誤って準備完了と報告される状況が他にもある可能性があります。したがって、ブロックしてはならないソケットでは O_NONBLOCK を使用する方が安全かもしれません。
2011 年 6 月 18 日土曜日に lkml でこれについての議論がありました。1 人のカーネル ハッカーが非 POSIX 準拠を正当化しようとしました。彼らは、都合のよいときは POSIX を尊重し、そうでないときはそれを冒涜します。
彼は、「2 人の読者がいて、2 番目の読者がブロックする可能性がある」と主張しました。しかし、そのようなアプリケーションの欠陥は非公平です。カーネルは、アプリケーションの欠陥を防ぐことは期待されていません。カーネルには明確な義務があります。select() の後の最初の read() のすべての場合において、カーネルは少なくとも 1 バイト、EOF、またはエラーを返さなければなりません。しかし、絶対にブロックしないでください。write() に関しては、書き込む前にソケットが select() によって書き込み可能であると報告されているかどうかを常にテストする必要があります。これにより、少なくとも 1 バイトの書き込みまたはエラーの発生が保証されます。しかし、絶対にブロックしないでください。ブロックしないことを期待してやみくもに書かないでください。まれなケースなどに関する Linux ハッカーの不平は、「難しい問題に取り組むには怠惰すぎる」という婉曲表現です。
次のように設定されたシリアルポートを読み取るとします。
最小 N; -icanon を使用して、読み取りを完了するために最小 N 文字を設定します
時間 N; -icanon を使用して、読み取りタイムアウトを 10 分の N 秒に設定します
分 250 時間 1
ここでは、250 文字のブロック、または 10 分の 1 秒のタイムアウトが必要です。これを非ブロッキング モードの Linux で試したところ、1 文字ごとに読み取りが返され、CPU に負荷がかかりました。文書化された動作を得るには、ブロック モードのままにしておく必要がありました。
そのため、select() でブロッキング モードを使用し、カーネルが POSIX 準拠であることを期待する十分な理由があります。
しかし、Linux を使用しなければならない場合は、Jeremy のアドバイスが Linux のカーネルの欠陥に対処するのに役立つかもしれません。
場合によります。ソケットを非ブロッキングとして設定すると、いくつかのことが行われます。
ソケットで読み取ることができるものがない場合、ブロックするのではなく、データなしですぐにread()
/を返します。recv()
を使用している場合select()
、これはおそらく問題ではありません。ソケットが読み取り可能であると通知されたときにのみソケットから読み取る限り、select()
問題ありません。
カーネルバッファーに十分なスペースがない場合、ブロックする代わりに、部分的な (またはゼロの) 書き込みを作成write()
/返します。send()
これはトリッキーです。アプリケーションがこの状況を処理するように作成されている場合、クライアントの読み取りが遅いときにアプリケーションがブロックされないことを意味するため、それは素晴らしいことです。ただし、アプリケーションは、ソケットに直接書き込むのではなく、書き込み可能なデータを独自のアプリケーション レベルのバッファに一時的に格納し、書き込みが保留中のソケットをwritefds
セットに選択的に配置する必要があることを意味します。アプリケーションが何であるかに応じて、これは命の恩人になることもあれば、非常に複雑になることもあります。慎重に選択してください。
ソケットが接続される前に設定された場合connect()
、接続が実際に行われる前に、すぐに戻ります。
同様に、これは、他のソケットで応答を続けながら応答が遅い可能性のあるホストにアプリケーションが接続する必要がある場合に役立ちますが、これらの半分接続されたソケットの処理方法に注意しないと問題が発生する可能性があります。通常は回避するのが最善です (ソケットが接続された後、ソケットをブロックしないように設定するだけです)。
一般に、ソケットを で使用するためにソケットをノンブロッキングに設定する必要はありませんselect()
。システム コールを使用すると、基本的なノンブロッキング方式でソケットを処理できます。ただし、一部のアプリケーションでは非ブロック書き込みが必要であり、そのためにフラグがまだ必要です。
通常、select() を使用しているときは、それをイベント ループの基礎として使用しています。また、イベント ループを使用する場合、イベント ループを select() 内でのみブロックし、それ以外の場所ではブロックしないようにします。(その理由は、プログラムが処理しているソケットのいずれかで何かを行う必要があるときはいつでも、プログラムが常に起動するようにするためです。たとえば、プログラムがソケット A の recv() 内でブロックされている場合、次のようになります。最初にソケット A からデータを取得して起動するまで、ソケット B に着信するデータを処理することはできず、その逆も同様です)。
したがって、select() を使用するときは、すべてのソケットを非ブロックに設定するのが最善です。そうすれば、プログラムが単一のソケットでブロックされ、他のソケットを長時間無視する可能性がなくなります。
send() と write() は、ソケットの送信バッファに収まりきらないほど多くのデータを提供するとブロックします。通常、select() プログラミングでは、select() 以外の場所をブロックしたくないため、ノンブロッキング モードを使用します。
特定の Windows API では、ノンブロッキング モードを使用することが非常に重要です。