サーチ…
構文
- 符号なしアラーム(符号なし秒)。
- int kill(pid_t pid、int sig);
パラメーター
関数、パラメータ、戻り値 | 説明 |
---|---|
alarm() | 関数名 |
unsigned seconds | アラームを発生させる秒数または保留中のアラームをキャンセルする0 |
> = 0 | 他のアラームが保留されていない場合は0、保留中のアラームがまだ開いていた秒数。この機能は失敗しません。 |
- | - |
kill() | 関数名 |
pid_t pid | 。 |
int sig | 0またはシグナルID |
0、-1 | 成功すると0が返され、 errno をEINVAL 、 EPERM またはESRCH errno すると-1が失敗します。 |
デフォルトアクションでSIGALARMを発生させる
alarm
を使用すると、指定した間隔の後にSIGALARM
信号を発生させるようにスケジュールできます。ユーザーがこのシグナルに対してブロックされていない、無視されている、または明示的なシグナルハンドラを指定していない場合、このシグナルのデフォルトアクションは到着時に実行されます。 仕様ごとにSIGALARM
デフォルトアクションはプロセスを終了することです:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char** argv)
{
printf("Hello!\n");
// Set the alarm for five second
alarm(5); // Per POSIX, this cannot fail
// Now sleep for 15 seconds
for (int i = 1; i <= 15; i++)
{
printf("%d\n", i);
sleep(1);
}
// And print the message before successful exit
printf("Goodbye!\n");
return EXIT_SUCCESS;
}
これは、
Hello!
1
2
3
4
5
[2] 35086 alarm ./a.out
sigactionを使ってシグナルハンドラを設定し、raiseを使ってシグナルを上げる
プログラムが特定のシグナルに反応するためには、デフォルトアクションを使用する以外に、 sigaction
を使用してカスタムシグナルハンドラをインストールすることができます。 sigaction
は3つの引数を受け取ります - シグナルに作用するシグナル、 sigaction_t
構造体へのポインタ、 NULL
でない場合は新しい動作を記述し、 sigaction_t
へのポインタです。もしNULL
はない場合は古い動作で満たされNULL
。同じプロセスでシグナルを発生させるには、 raise
メソッドを使用します。より多くの制御が必要な場合(シグナルを他のプロセスに送るには、 kill
またはpthread_kill
を使用できます。これは、宛先プロセスIDまたはスレッドIDを受け入れます)。
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Signals are numbered from 1, signal 0 doesn't exist
volatile sig_atomic_t last_received_signal = 0;
// Signal handler, will set the global variable
// to indicate what is the last signal received.
// There should be as less work as possible inside
// signal handler routine, and one must take care only
// to call reentrant functions (in case of signal arriving
// while program is already executing same function)
void signal_catcher(int signo, siginfo_t *info, void *context)
{
last_received_signal = info->si_signo;
}
int main (int argc, char** argv)
{
// Setup a signal handler for SIGUSR1 and SIGUSR2
struct sigaction act;
memset(&act, 0, sizeof act);
// sigact structure holding old configuration
// (will be filled by sigaction):
struct sigaction old_1;
memset(&old_1, 0, sizeof old_1);
struct sigaction old_2;
memset(&old_2, 0, sizeof old_2);
act.sa_sigaction = signal_catcher;
// When passing sa_sigaction, SA_SIGINFO flag
// must be specified. Otherwise, function pointed
// by act.sa_handler will be invoked
act.sa_flags = SA_SIGINFO;
if (0 != sigaction(SIGUSR1, &act, &old_1))
{
perror("sigaction () failed installing SIGUSR1 handler");
return EXIT_FAILURE;
}
if (0 != sigaction(SIGUSR2, &act, &old_2))
{
perror("sigaction() failed installing SIGUSR2 handler");
return EXIT_FAILURE;
}
// Main body of "work" during which two signals
// will be raised, after 5 and 10 seconds, and which
// will print last received signal
for (int i = 1; i <= 15; i++)
{
if (i == 5)
{
if (0 != raise(SIGUSR1))
{
perror("Can't raise SIGUSR1");
return EXIT_FAILURE;
}
}
if (i == 10)
{
if (0 != raise(SIGUSR2))
{
perror("Can't raise SIGUSR2");
return EXIT_FAILURE;
}
}
printf("Tick #%d, last caught signal: %d\n",
i, last_received_signal);
sleep(1);
}
// Restore old signal handlers
if (0 != sigaction(SIGUSR1, &old_1, NULL))
{
perror("sigaction() failed restoring SIGUSR1 handler");
return EXIT_FAILURE;
}
if (0 != sigaction(SIGUSR2, &old_2, NULL))
{
perror("sigaction() failed restoring SIGUSR2 handler");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
これは、
Tick #1, last caught signal: 0
Tick #2, last caught signal: 0
Tick #3, last caught signal: 0
Tick #4, last caught signal: 0
Tick #5, last caught signal: 30
Tick #6, last caught signal: 30
Tick #7, last caught signal: 30
Tick #8, last caught signal: 30
Tick #9, last caught signal: 30
Tick #10, last caught signal: 31
Tick #11, last caught signal: 31
Tick #12, last caught signal: 31
Tick #13, last caught signal: 31
Tick #14, last caught signal: 31
Tick #15, last caught signal: 31
kill()を使って自殺するプロセス
プロセスは、 kill()
関数を使って他のプロセスにシグナルを送信(試行)することができます。
そのためには、送信プロセスは受信プロセスのPIDを知る必要があります。レースを導入することなく、プロセスは独自のPID(およびその子プロセスのPID)のみを確認することができるkill()
、 kill()
使用方法を示す最も簡単な例は、プロセスがシグナルを自身に送信することです。
killシグナル( SIGKILL
)を送信して自身の終了を開始するプロセスの例を以下に示します。
#define _POSIX_C_SOURCE 1
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int main (void)
{
pid_t pid = getpid(); /* Get my iown process ID. */
kill(pid, SIGKILL); /* Send myself a KILL signal. */
puts("Signal delivery initiated."); /* Although not guaranteed,
practically the program never gets here. */
pause(); /* Wait to die. */
puts("This never gets printed.");
}
出力:
Killed
(...または同様に、実装に応じて)
スレッドセーフな方法でwrite()によって生成されたSIGPIPEを処理する
読み込み終了が閉じられている名前付きまたは名前のないパイプまたはストリームソケットに対してwrite()
が呼び出されると、次の2つのことが起こります。
-
SIGPIPE
シグナルはwrite()
を呼び出したプロセスに送られ、
-
SIGPIPE
シグナルはwrite()
を呼び出したスレッドに送られ、
-
write()
によってEPIPE
エラーが返されwrite()
SIGPIPE
を扱うにはいくつかの方法があります:
- ソケットの場合、Linuxでは
SO_NOSIGPIPE
、BSDではMSG_NOSIGNAL
ようなプラットフォーム固有のオプションを設定することで、SIGPIPE
を無効にすることができます(send
ではのみ動作しsend
が、write
では動作しません)。これは移植性がありません。
- FIFO(名前付きパイプ)では、ライターが
O_WRONLY
ではなくO_RDWR
を使用するとSIGPIPE
は生成されないため、読み取り終了は常に開かれます。しかし、これもEPIPE
無効にします。
-
SIGPIPE
を無視したり、グローバルハンドラを設定することができます。これは良い解決策ですが、アプリケーション全体を制御していない場合(例えば、ライブラリを作成している場合など)は受け入れられません。
- 最近のPOSIXバージョンでは、
SIGPIPE
がwrite()
を呼び出したスレッドに送信し、同期信号処理技術を使用して処理するという事実を利用できます。
以下のコードは、POSIX.1-2004以降のスレッドセーフなSIGPIPE
処理を示しています。
- まず、
pthread_sigmask()
を使って現在のスレッドのシグナルマスクにSIGPIPE
を追加します。 - すでに
sigpending()
を使用して保留中のSIGPIPE
があるかどうかを確認してください。 -
write()
呼び出しwrite()
。読み取り終了がクローズされていると、保留中のシグナルmaskにSIGPIPE
が追加され、EPIPE
が返されます。 -
write()
がEPIPE
返し、SIGPIPE
がwrite()
前にまだ保留されていないSIGPIPE
は、sigtimedwait()
を使用して保留中のシグナルマスクから削除します。 -
pthread_sigmask()
を使用して元のシグナルマスクを復元します。
ソースコード:
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <sys/signal.h>
ssize_t safe_write(int fd, const void* buf, size_t bufsz)
{
sigset_t sig_block, sig_restore, sig_pending;
sigemptyset(&sig_block);
sigaddset(&sig_block, SIGPIPE);
/* Block SIGPIPE for this thread.
*
* This works since kernel sends SIGPIPE to the thread that called write(),
* not to the whole process.
*/
if (pthread_sigmask(SIG_BLOCK, &sig_block, &sig_restore) != 0) {
return -1;
}
/* Check if SIGPIPE is already pending.
*/
int sigpipe_pending = -1;
if (sigpending(&sig_pending) != -1) {
sigpipe_pending = sigismember(&sig_pending, SIGPIPE);
}
if (sigpipe_pending == -1) {
pthread_sigmask(SIG_SETMASK, &sig_restore, NULL);
return -1;
}
ssize_t ret;
while ((ret = write(fd, buf, bufsz)) == -1) {
if (errno != EINTR)
break;
}
/* Fetch generated SIGPIPE if write() failed with EPIPE.
*
* However, if SIGPIPE was already pending before calling write(), it was
* also generated and blocked by caller, and caller may expect that it can
* fetch it later. Since signals are not queued, we don't fetch it in this
* case.
*/
if (ret == -1 && errno == EPIPE && sigpipe_pending == 0) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
int sig;
while ((sig = sigtimedwait(&sig_block, 0, &ts)) == -1) {
if (errno != EINTR)
break;
}
}
pthread_sigmask(SIG_SETMASK, &sig_restore, NULL);
return ret;
}