パイプで select() を使用する- これが私がやっていることであり、今それをキャッチする必要がありSIGTERM
ます。どうすればできますか?select()
エラー (< 0) が返されたときにそれを行う必要がありますか?
3 に答える
まず、キャッチされない場合はプロセスSIGTERM
を強制終了select()
し、戻りません。したがって、 のシグナル ハンドラをインストールする必要がありますSIGTERM
。を使用してそれを行いsigaction()
ます。
ただし、スレッドが でブロックされていないSIGTERM
瞬間にシグナルが到着する可能性があります。プロセスがファイル記述子でほとんどスリープしている場合、これはまれな状態ですが、それ以外の場合は発生する可能性があります。これは、シグナル ハンドラーが割り込みをメイン ルーチンに通知するために何かを行う必要があることを意味します。つまり、何らかのフラグ変数 (タイプ ) を設定するか、プロセスが でスリープしているときにのみ配信されることを保証する必要があります。select()
sig_atomic_t
SIGTERM
select()
柔軟性は劣りますが、後者のアプローチの方が単純なので、私は後者のアプローチを採用します (記事の最後を参照)。
したがって、 をSIGTERM
呼び出す直前にブロックselect()
し、関数が戻った直後に再ブロックして、プロセスが内部でスリープしている間だけシグナルを受信するようにしますselect()
。ただし、これにより実際には競合状態が発生することに注意してください。unblock の直後で が呼び出される直前にシグナルが到着した場合、select()
システム コールはまだ呼び出されていないため、 は返されません-1
。select()
正常に戻った直後で、再ブロックの直前に信号が到着した場合は、信号も失われています。
したがって、そのために使用する必要がありますpselect()
。select()
アトミックにブロック/ブロック解除を行います。
まず、ループに入る前にSIGTERM
使用をブロックします。その後、によって返された元のマスクを使用して呼び出すだけです。このようにして、 でスリープしている間だけプロセスが中断されることを保証します。sigprocmask()
pselect()
pselect()
sigprocmask()
select()
要約すれば:
SIGTERM
(何もしない);のハンドラーをインストールします。pselect()
ループに入る前に、;SIGTERM
を使用してブロックします。sigprocmask()
pselect()
によって返された古いシグナル マスクを使用して呼び出しsigprocmask()
ます。- ループ内で、返されたとが である
pselect()
かどうかを安全に確認できるようになりました。pselect()
-1
errno
EINTR
pselect()
正常に返された後、多くの作業を行うと、応答するときに大きな遅延が発生する可能性があることに注意してください(プロセスはすべての処理を実行し、実際に信号を処理SIGTERM
する前にに戻る必要があるため)。pselect()
これが問題になる場合は、シグナル ハンドラー内でフラグ変数を使用する必要があります。これにより、コード内のいくつかの特定のポイントでこの変数を確認できます。ただし、フラグ変数を使用しても競合状態がなくなるわけではなく、必要がなくなるわけではありませんpselect()
。
覚えておいてください:ファイル記述子やシグナルの配信を待つ必要があるときはいつでも、 (または、それをサポートするシステムの場合は ) を使用する必要があります。pselect()
ppoll()
編集:使用法を説明するためのコード例に勝るものはありません。
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
// Signal handler to catch SIGTERM.
void sigterm(int signo) {
(void)signo;
}
int main(void) {
// Install the signal handler for SIGTERM.
struct sigaction s;
s.sa_handler = sigterm;
sigemptyset(&s.sa_mask);
s.sa_flags = 0;
sigaction(SIGTERM, &s, NULL);
// Block SIGTERM.
sigset_t sigset, oldset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, &oldset);
// Enter the pselect() loop, using the original mask as argument.
fd_set set;
FD_ZERO(&set);
FD_SET(0, &set);
while (pselect(1, &set, NULL, NULL, NULL, &oldset) >= 0) {
// Do some processing. Note that the process will not be
// interrupted while inside this loop.
sleep(5);
}
// See why pselect() has failed.
if (errno == EINTR)
puts("Interrupted by SIGTERM.");
else
perror("pselect()");
return EXIT_SUCCESS;
}