Сигналы в Linux
🧩 Определение
Сигнал — это асинхронное программное прерывание, используемое ядром Linux для взаимодействия между процессами или между ядром и процессом.
Он служит для уведомления о событии, требующем немедленной реакции, аналогично аппаратным прерываниям на уровне процессора.
⚙️ Классификация сигналов
- Системные - Управление жизненным циклом процессов
- Ошибочные - Сообщают об ошибках выполнения
- Пользовательские - Для пользовательских уведомлений
- Реального времени (RT) - Очередные и упорядоченные сигналы
🧠 Механизм обработки
Каждый процесс имеет таблицу обработчиков сигналов, связанную со структурой task_struct.
При поступлении сигнала ядро помещает его в очередь ожидания (pending queue), и при переходе в пользовательский режим процесс выполняет соответствующий обработчик.
Этапы:
- Генерация — сигнал создаётся (
kill(),raise(),alarm(), системное событие). - Доставка — ядро ставит сигнал в очередь процесса.
- Обработка — выполняется действие по умолчанию или установленный обработчик (
signal()/sigaction()).
📡 Примеры сигналов
| Сигнал | Константа | Описание | Типичная ситуация |
|---|---|---|---|
| 1 | SIGHUP | Потеря управляющего терминала | Завершение сессии |
| 2 | SIGINT | Прерывание с клавиатуры | Ctrl+C |
| 9 | SIGKILL | Безусловное завершение | Завершение зависшего процесса |
| 11 | SIGSEGV | Ошибка сегментации | Недопустимый доступ к памяти |
| 15 | SIGTERM | Корректное завершение | Стандартный запрос на остановку |
| 17 | SIGCHLD | Потомок завершился | Оповещение родителя |
| 30–64 | SIGRTMIN–SIGRTMAX | Реальное время | Пользовательские уведомления |
Примеры:
kill(1234, SIGTERM); // Отправить сигнал процессу
raise(SIGUSR1); // Послать сигнал самому себе
signal(SIGINT, handler); // Установить обработчик
💻 Примеры системных вызовов
| Категория | Системный вызов | Назначение | Пользовательский аналог |
|---|---|---|---|
| Файлы | open(), read(), write(), close() | Работа с файловыми дескрипторами | fopen(), fread() |
| Процессы | fork(), execve(), waitpid() | Управление процессами | system() |
| Память | mmap(), brk(), munmap() | Управление виртуальной памятью | malloc() |
| Время | nanosleep(), clock_gettime() | Таймеры и задержки | sleep() |
| IPC | pipe(), msgsnd(), semop() | Межпроцессное взаимодействие | POSIX IPC API |
| Сеть | socket(), connect(), send(), recv() | Сетевые операции | BSD sockets API |
Пример:
#include <unistd.h>
#include <sys/wait.h>
pid_t pid = fork();
if (pid == 0) {
execl("/bin/ls", "ls", "-l", NULL);
} else {
waitpid(pid, NULL, 0);
}
⚖️ Сравнение: Сигналы vs. Системные вызовы
| Критерий | Сигнал | Системный вызов |
|---|---|---|
| Природа | Асинхронное прерывание | Синхронный вызов ядра |
| Инициатор | Ядро или другой процесс | Сам процесс |
| Направление | Ядро → процесс | Процесс → ядро |
| Контроль | Неконтролируемый (асинхронный) | Контролируемый (синхронный) |
| Назначение | Уведомление о событии | Запрос системной операции |
| Передача управления | В произвольный момент (через обработчик) | По вызову процесса |
| Пример | SIGSEGV, SIGKILL, SIGUSR1 | read(), fork(), execve() |
📚 Академическое резюме
Сигналы реализуют асинхронную передачу управления и механизм внутрисистемного уведомления о событиях.
Системные вызовы — это синхронные точки входа в ядро, предоставляющие процессу контролируемый доступ к ресурсам операционной системы.
🧠 Итог:
Сигнал — это вмешательство ядра в выполнение процесса.
Системный вызов — это инициатива процесса на обращение к ядру.