(p)select と (p)poll の違いは微妙です:
select の場合、select を呼び出す前に、醜い fd_set ビットマップを初期化して設定する必要があります。 (投票は .events
を区別します と .revents
struct pollfd
のメンバー ).
選択後、ほとんどの fd が監視されていなくても、ビットマップ全体が (人/コードによって) イベントをスキャンされることがよくあります。
第三に、ビットマップは、数が特定の制限 (現在の実装:1024..4096 の間のどこか) 未満の fds のみを処理できます。すでに代わりに epoll を使用しています)。
select()
との比較を開始することをお勧めします vs poll()
. Linux では pselect()
も提供しています。 と ppoll()
;そして余分な const sigset_t *
pselect()
への引数 と ppoll()
(vs select()
と poll()
) いわば、各「p バリアント」に同じ効果があります。シグナルを使用していない場合は、保護する競争がないため、基本的な質問はプログラミングの効率と容易さについてです。
一方、stackoverflow.com の回答は既にここにあります:poll と select の違いは何ですか。
レースに関しては、(何らかの理由で) シグナルを使い始めると、一般的に、シグナル ハンドラーは volatile sig_atomic_t
型の変数を設定するだけでよいことがわかります。 信号が検出されたことを示します。これの基本的な理由は、多くのライブラリ呼び出しが再入可能ではなく、そのようなルーチンの「途中」でもシグナルが配信される可能性があるためです。たとえば、 stdout
などのストリーム スタイルのデータ構造にメッセージを出力するだけです。 (C) または cout
(C++) 再入の問題が発生する可能性があります。
volatile sig_atomic_t flag
を使用するコードがあるとします。 変数、おそらく SIGINT
をキャッチするため 、このようなもの (http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html も参照):
volatile sig_atomic_t got_interrupted = 0;
void caught_signal(int unused) {
got_interrupted = 1;
}
...
struct sigaction sa;
sa.sa_handler = caught_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sa, NULL) == -1) ... handle error ...
...
ここで、コードの本体で、「中断されるまで実行」したい場合があります:
while (!got_interrupted) {
... do some work ...
}
select
のように、何らかの入出力を待機する呼び出しを行う必要が生じるまでは、これで問題ありません。 または poll
. 「待機」アクションはその I/O を待機する必要がありますが、も SIGINT
を待つ必要があります 割り込み。単に書く場合:
while (!got_interrupted) {
... do some work ...
result = select(...); /* or result = poll(...) */
}
直前に割り込みが発生する可能性があります あなたは select()
に電話します または poll()
、後ではなく。この場合、割り込みが発生し、変数 got_interrupted
が 設定されます—しかし、その後、待機を開始します。 got_interrupted
を確認する必要がありました 後ではなく、待機を開始する前に変数を使用してください。
書いてみてください:
while (!got_interrupted) {
... do some work ...
if (!got_interrupted)
result = select(...); /* or result = poll(...) */
}
これにより、「レース ウィンドウ」が縮小されます。これは、「何らかの作業を行う」コードの実行中に割り込みが発生した場合に割り込みを検出するためです。しかし、割り込みが直後に発生する可能性があるため、競合はまだあります。 変数をテストしますが、直前 選択またはポーリング。
解決策は、sigprocmask
のシグナル ブロッキング プロパティを使用して、「テストしてから待機する」シーケンスを「アトミック」にすることです。 (または、POSIX スレッド コードでは、pthread_sigmask
):
sigset_t mask, omask;
...
while (!got_interrupted) {
... do some work ...
/* begin critical section, test got_interrupted atomically */
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (sigprocmask(SIG_BLOCK, &mask, &omask))
... handle error ...
if (got_interrupted) {
sigprocmask(SIG_SETMASK, &omask, NULL); /* restore old signal mask */
break;
}
result = pselect(..., &omask); /* or ppoll() etc */
sigprocmask(SIG_SETMASK, &omask, NULL);
/* end critical section */
}
(上記のコードは実際にはそれほど素晴らしいものではありません。効率よりも説明のために構造化されています。シグナル マスク操作を少し異なる方法で行い、「中断された」テストを別の方法で配置する方が効率的です)。
実際に SIGINT
をキャッチする必要があるまで ただし、比較する必要があるのは select()
だけです と poll()
(そして、多数の記述子が必要になった場合は、epoll()
のようなイベントベースのものをいくつか どちらよりも効率的です)。