Сигналы реального времени и «слипание»
Почему обычные сигналы слипаются
Ядро хранит для процесса (и потока) множество ожидающих сигналов как битовую маску: по биту на сигнал.
Пока сигнал не доставлен (заблокирован или процесс ещё не выполнялся), повторные экземпляры теряются — «слипаются» в один. Сигнал говорит только «событие произошло хотя бы раз».
Последний столбец задачи 1
«Не слипается» — это свойство сигналов реального времени. Из таблицы экзамена так ведёт себя только SIGRTMIN. Все остальные — SIGCHLD, SIGKILL, SIGABRT, SIGBUS, SIGUSR2, SIGTTIN, SIGSTOP, SIGFPE, SIGPIPE, SIGCONT — обычные и слипаются.
Следствие: обработчик SIGCHLD
Если три потомка завершились почти одновременно, родитель может получить SIGCHLD один раз. Поэтому правильный обработчик собирает всех:
void on_chld(int sig)
{
int saved = errno;
while (waitpid(-1, NULL, WNOHANG) > 0)
; /* собрать всех завершившихся */
errno = saved;
}
Сигналы реального времени
POSIX.1b добавил диапазон SIGRTMIN…SIGRTMAX (в Linux — 32–64, glibc резервирует пару первых для себя, поэтому используйте только макросы SIGRTMIN + n).
Отличия от обычных:
| Свойство | Обычные | Реального времени |
|---|---|---|
| Повторы до доставки | слипаются | ставятся в очередь |
| Данные | нет | union sigval: int или void * |
| Порядок | не определён | по возрастанию номера; одного номера — в порядке отправки |
| Смысл | предопределён | задаёт программа |
| По умолчанию | разное | завершить (Term) |
Отправка с данными
union sigval v;
v.sival_int = 42;
sigqueue(pid, SIGRTMIN, v);
Приём с информацией
void handler(int sig, siginfo_t *info, void *ctx)
{
/* info->si_value.sival_int — переданное число
info->si_pid — кто послал, info->si_code == SI_QUEUE */
}
struct sigaction sa = {0};
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO; /* обработчик с тремя аргументами */
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL);
Можно и синхронно, без обработчика: заблокировать сигнал и забирать его sigwaitinfo(&set, &info) или читать через signalfd.
Очередь не бесконечна
Число ожидающих сигналов реального времени ограничено лимитом RLIMIT_SIGPENDING (ulimit -i). Если очередь полна, sigqueue вернёт EAGAIN.
Демонстрация
/* родитель блокирует сигналы, потомок посылает по 5 штук */
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGRTMIN);
sigprocmask(SIG_BLOCK, &set, NULL);
if (fork() == 0) {
union sigval v = {0};
for (int i = 0; i < 5; i++) {
kill(getppid(), SIGUSR1);
sigqueue(getppid(), SIGRTMIN, v);
}
_exit(0);
}
wait(NULL);
sigprocmask(SIG_UNBLOCK, &set, NULL);
/* обработчик SIGRTMIN вызовется 5 раз, SIGUSR1 — 1 раз */
Итог
- Обычные ожидающие сигналы — биты маски; повторы до доставки слипаются.
- Сигналы реального времени (
SIGRTMIN…SIGRTMAX) ставятся в очередь, несут данные и доставляются по порядку. - Отправка —
sigqueue, приём данных —SA_SIGINFOиsiginfo_t. - Обработчик
SIGCHLDдолжен собирать потомков в циклеwaitpid(..., WNOHANG).
Практика решено 0 из 5
Процесс заблокировал SIGUSR1, и за это время ему послали SIGUSR1 пять раз. Сколько раз сработает обработчик после снятия блокировки?
Тот же опыт, но с сигналом SIGRTMIN, посланным 5 раз через sigqueue. Сколько раз сработает обработчик?
Пошлите процессу pid сигнал SIGRTMIN с числом 42 в качестве данных через sigqueue.
Используйте union sigval v; и поле v.sival_int.
union sigval v;
v.sival_int = 42;
sigqueue(pid, SIGRTMIN, v);
Сравните обычные сигналы и сигналы реального времени.
| Обычные (1–31) | Реального времени (SIGRTMIN…SIGRTMAX) | |
|---|---|---|
| Повторные экземпляры, пришедшие до доставки, «слипаются» | ||
| Каждый экземпляр ставится в очередь | ||
| Могут нести данные (число или указатель) | ||
| У каждого предопределённый смысл (SIGINT, SIGCHLD...) | ||
| Порядок доставки гарантирован: меньший номер — раньше | ||
| Действие по умолчанию — завершить процесс |
Родитель получил SIGCHLD один раз. Сколько потомков завершилось?
Войдите, чтобы отмечать пройденные уроки.