Поиск…


Синтаксис

  • неподписанный сигнал тревоги (беззнаковые секунды);
  • int kill (pid_t pid, int sig);

параметры

Функция, Параметр (ы), Возвращаемое значение Описание
alarm() имя функции
unsigned seconds Секунды, чтобы поднять тревогу или 0, чтобы отменить любой ожидающий сигнал тревоги
> = 0 0, если другой сигнал не был отложен, иначе количество секунд, которое ожидающий сигнал тревоги остался открытым. Эта функция не сработает.
- -
kill() имя функции
pid_t pid ,
int sig 0 или идентификатор сигнала
0, -1 При успешном ESRCH возвращается 0, -1 при ESRCH с установкой errno в EINVAL , EPERM или ESRCH .

Поднятие SIGALARM с действием по умолчанию

Используя alarm , пользователь может назначить сигнал SIGALARM после определенного интервала. В случае, если пользователь не заблокировал, проигнорировал или указал явный обработчик сигнала для этого сигнала, действие по умолчанию для этого сигнала будет выполняться по прибытии. В соответствии со спецификацией по умолчанию для 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 . sigaction получает три аргумента - сигнал действовать, указатель на структуру sigaction_t который, если не NULL , описывает новое поведение и указатель на sigaction_t который, если не NULL будет заполнен старым поведением (поэтому его можно восстановить). Поднять сигналы в том же процессе можно с помощью метода raise . Если требуется больше контроля (чтобы отправить сигнал на какой-либо другой процесс, можно использовать kill или pthread_kill , которые принимают идентификатор процесса назначения или идентификатор потока).

#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-signal ( 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

(... или аналогично, в зависимости от реализации)

Обрабатывать SIGPIPE, сгенерированный write () потокобезопасным способом

Когда write() вызывается для именованного или неназванного канала или потока, чей конец чтения закрыт, происходят две вещи:

POSIX.1-2001
  1. Сигнал SIGPIPE отправляется процессу, который вызывает write()
POSIX.1-2004
  1. Сигнал SIGPIPE отправляется в поток, который вызывает write()
  1. Ошибка EPIPE возвращается командой write()

Существует несколько способов решения SIGPIPE :

  • Для сокетов SIGPIPE можно отключить, установив параметры платформы, такие как MSG_NOSIGNAL в Linux и SO_NOSIGPIPE в BSD (работает только для send , но не для write ). Это не переносимо.
  • Для FIFO (именованные каналы) SIGPIPE не будет сгенерирован, если автор использует O_RDWR вместо O_WRONLY , так что конец чтения всегда открывается. Однако это также отключает EPIPE .
  • Мы можем игнорировать SIGPIPE или устанавливать глобальный обработчик. Это хорошее решение, но это неприемлемо, если вы не контролируете все приложение (например, вы пишете библиотеку).
  • С недавними версиями POSIX мы можем использовать тот факт, что SIGPIPE отправляется в поток, который вызывает write() и обрабатывает его с использованием технологии синхронного управления сигналами.

В приведенном ниже коде демонстрируется поточно-безопасная обработка SIGPIPE для POSIX.1-2004 и более поздних версий.

Это вдохновляет этот пост :

  • Сначала добавьте SIGPIPE чтобы сигнализировать маску текущего потока, используя pthread_sigmask() .
  • Убедитесь, что SIGPIPE уже находится на рассмотрении, используя sigpending() .
  • Вызов write() . Если конец чтения закрыт, SIGPIPE будет добавлен в маску ожидающих сигналов, и EPIPE будет возвращен.
  • Если write() возвратил EPIPE , а SIGPIPE не был EPIPE до write() , удалите его из маски ожидающих сигналов, используя 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