Сигналы — короткое сообщение, посылаемое процессу, или группе процессов, чтобы:
- Уведомить об определённом событии.
- Заставить выполнить обработчик сигнала.
По мимо обычных сигналов, есть сигналы реального времени, их отличие от обычных, что они всегда ставятся в очередь, а потому процесс сможет их все обработать, в то время как с обычными сигналами не всегда.
- kill( ) — Send a signal to a thread group.
- tkill( ) — Send a signal to a process.
- tgkill( ) — Send a signal to a process in a specific thread group.
- sigaction( ) — Change the action associated with a signal.
- signal( ) — Similar to sigaction( ).
- sigpending( ) — Check whether there are pending signals. (Проверка необработанных сигналов)
- sigprocmask( ) — Modify the set of blocked signals(заблокированных сигналов).
- sigsuspend( ) — Wait for a signal
Нужно разделять 2 задачи: отправка и обработка сигнала ядром. Каждая из этих задач требует отдельного подхода:
- Генерация сигнала — ядро изменяет структуру данных процесса, что означает отправку сигнала.
- Доставка сигнала — реакция на сигнал, которая может наступить далеко не сразу. Например, потому что процесс заблокировал обработку сигнала, или потому что мы уже в обработчике данного сигнала, обработчики обычных сигналов не должны быть рентабельными.
Реакция на доставленный сигнал зависит от ситуации и сигнала:
- Игнорирование сигнала, не каждый сигнал может быть проигнорирован, например, SIGKILL, SIGSTOP.
- Выполнить действие по умолчанию: завершить выполнение, сделать дамп, игнорировать, остановить, возобновить исполнение.
- Вызвать обработчик сигнала. Не для всех сигналов можно назначить свой обработчик сигнала.
Для отправки сигнала нужен системный вызов sys_kill(). Когда он завершается, это значит только, что сигнал отправлен и больше ничего. Далее находится pid и соответсвующий task_struct. После чего, зовётся kik_task() - функция, которая будет процесс, и начинается этап доставки сигнала. (!TODO)
Чтобы обработать сигналы вызывается функция — do_signal(). Обработка сигнала откладывается прямо до возвращения в пользовательский режим, потому что только в этот момент есть гарантия того, что все lock’и отпущены и структуры данных свободны. Если для сигнала был задан обработчик, то do_signal() должна его позвать. Но тут всё не просто в работе со стеком.
- Перед возвращение в пользовательский режим ядро зовёт функцию — do_signal().
- Которая обрабатывает сигнал, вызывая handle_signal(), которая настраивает стэк режима пользователя, вызывая setup_frame(), который копирует стэк аппаратного режима в стэк пользователя и записывает адрес завершающей функции sys_sigreturn.
- По возвращению в процесс пользователя, он начинает обработку сигнала, так как rip = handler.
- Когда обработчик завершиться на вершине стока будет адрес кода, который был помещён туда функцией setup_frame() и который должен быть выполнен после обработки сигнала.
- Выполнить системный вызов, вызывающий функцию sys_sigreturn, которая восстанавливает первоначальный контекст.
Повторное выполнение системных вызовов (!TODO) restartable call uses - restart_block - on task_struct - number of system call and parameters.