Поиск…
Синтаксис
- неподписанный сигнал тревоги (беззнаковые секунды);
- 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()
вызывается для именованного или неназванного канала или потока, чей конец чтения закрыт, происходят две вещи:
- Сигнал
SIGPIPE
отправляется процессу, который вызываетwrite()
- Сигнал
SIGPIPE
отправляется в поток, который вызываетwrite()
- Ошибка
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;
}