サーチ…


構文

  • 符号なしアラーム(符号なし秒)。
  • int kill(pid_t pid、int sig);

パラメーター

関数、パラメータ、戻り値説明
alarm() 関数名
unsigned seconds アラームを発生させる秒数または保留中のアラームをキャンセルする0
> = 0 他のアラームが保留されていない場合は0、保留中のアラームがまだ開いていた秒数。この機能は失敗しません。
- -
kill() 関数名
pid_t pid
int sig 0またはシグナルID
0、-1 成功すると0が返され、 errnoEINVALEPERMまたは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つのことが起こります。

POSIX.1-2001
  1. SIGPIPEシグナルはwrite()を呼び出したプロセスに送られ、
POSIX.1-2004
  1. SIGPIPEシグナルはwrite()を呼び出したスレッドに送られ、
  1. write()によってEPIPEエラーが返されwrite()

SIGPIPEを扱うにはいくつかの方法があります:

  • ソケットの場合、LinuxではSO_NOSIGPIPE 、BSDではMSG_NOSIGNALようなプラットフォーム固有のオプションを設定することで、 SIGPIPEを無効にすることができます( sendではのみ動作しsendが、 writeでは動作しません)。これは移植性がありません。
  • FIFO(名前付きパイプ)では、ライターがO_WRONLYではなくO_RDWRを使用するとSIGPIPEは生成されないため、読み取り終了は常に開かれます。しかし、これもEPIPE無効にします。
  • SIGPIPEを無視したり、グローバルハンドラを設定することができます。これは良い解決策ですが、アプリケーション全体を制御していない場合(例えば、ライブラリを作成している場合など)は受け入れられません。
  • 最近のPOSIXバージョンでは、 SIGPIPEwrite()を呼び出したスレッドに送信し、同期信号処理技術を使用して処理するという事実を利用できます。

以下のコードは、POSIX.1-2004以降のスレッドセーフなSIGPIPE処理を示しています。

この投稿にインスパイアされています

  • まず、 pthread_sigmask()を使って現在のスレッドのシグナルマスクにSIGPIPEを追加します。
  • すでにsigpending()を使用して保留中のSIGPIPEがあるかどうかを確認してください。
  • write()呼び出しwrite() 。読み取り終了がクローズされていると、保留中のシグナルmaskにSIGPIPEが追加され、 EPIPEが返されます。
  • write()EPIPE返し、 SIGPIPEwrite()前にまだ保留されていない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;
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow