Сигналы реального времени и «слипание»

⏱ 25 мин · 🧩 заданий: 5

Почему обычные сигналы слипаются

Ядро хранит для процесса (и потока) множество ожидающих сигналов как битовую маску: по биту на сигнал.

pending (обычные сигналы): бит на сигнал номер: 1 2 3 ... 10 11 12 ... 17 ... 31 [0 0 0 ... 1 0 0 ... 1 ... 0] ▲ ▲ SIGUSR1 ожидает SIGCHLD ожидает kill(pid, SIGUSR1) × 5 → бит 10 = 1 (уже был 1 — ничего не меняется) доставка → обработчик вызван 1 раз, бит сброшен

Пока сигнал не доставлен (заблокирован или процесс ещё не выполнялся), повторные экземпляры теряются — «слипаются» в один. Сигнал говорит только «событие произошло хотя бы раз».

Последний столбец задачи 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Один вариант ответа

Процесс заблокировал SIGUSR1, и за это время ему послали SIGUSR1 пять раз. Сколько раз сработает обработчик после снятия блокировки?

Задание 2 Один вариант ответа

Тот же опыт, но с сигналом SIGRTMIN, посланным 5 раз через sigqueue. Сколько раз сработает обработчик?

Задание 3 Написать код

Пошлите процессу pid сигнал SIGRTMIN с числом 42 в качестве данных через sigqueue. Используйте union sigval v; и поле v.sival_int.

Задание 4 Заполнить таблицу

Сравните обычные сигналы и сигналы реального времени.

Обычные (1–31)Реального времени (SIGRTMIN…SIGRTMAX)
Повторные экземпляры, пришедшие до доставки, «слипаются»
Каждый экземпляр ставится в очередь
Могут нести данные (число или указатель)
У каждого предопределённый смысл (SIGINT, SIGCHLD...)
Порядок доставки гарантирован: меньший номер — раньше
Действие по умолчанию — завершить процесс
Отметьте ячейку — «+», пустая — «−».
Задание 5 Один вариант ответа

Родитель получил SIGCHLD один раз. Сколько потомков завершилось?

Войдите, чтобы отмечать пройденные уроки.