Можно ли заблокировать сигналы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Блокировка сигналов в Unix/Linux
Да, в Unix/Linux можно и нужно блокировать сигналы (signal masking). Это один из самых важных механизмов для управления обработкой сигналов в многопоточных и критичных приложениях.
Концепция маски сигналов
Маска сигналов (signal mask) — это набор сигналов, доставка которых временно приостановлена. Заблокированный сигнал не игнорируется, а ставится в очередь и доставляется, когда будет разблокирован.
Блокировка сигналов через sigprocmask() (POSIX)
Основной механизм для однопоточных приложений:
#include <signal.h>
#include <unistd.h>
sigset_t set, oldset;
// Инициализируем пустой набор
sigemptyset(&set);
// Добавляем SIGTERM в набор
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGINT);
// Блокируем эти сигналы
sigprocmask(SIG_BLOCK, &set, &oldset); // SIG_BLOCK — добавляет к маске
// Критическая секция, где сигналы не будут обработаны
// ...
// Восстанавливаем исходную маску
sigprocmask(SIG_SETMASK, &oldset, nullptr);
Операции с маской
- SIG_BLOCK — добавить сигналы в маску (блокировать)
- SIG_UNBLOCK — удалить сигналы из маски (разблокировать)
- SIG_SETMASK — установить маску (заменить полностью)
Для многопоточных приложений: pthread_sigmask()
Для потокобезопасности используй pthread_sigmask():
#include <signal.h>
#include <pthread.h>
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGUSR1);
// Блокируем сигналы в этом потоке
pthread_sigmask(SIG_BLOCK, &set, nullptr);
// Сигналы будут доставлены другому потоку (если он их не заблокировал)
Критическое различие: sigprocmask() работает с процессом, pthread_sigmask() — с отдельным потоком.
Обработка заблокированных сигналов через sigwait()
Синхронная обработка вместо асинхронных обработчиков:
#include <signal.h>
#include <iostream>
void* signal_handler_thread(void* arg) {
sigset_t* set = (sigset_t*)arg;
int sig;
while (true) {
// Ждем сигнала синхронно
if (sigwait(set, &sig) == 0) {
std::cout << "Получен сигнал: " << sig << std::endl;
if (sig == SIGTERM) {
std::cout << "Завершение приложения" << std::endl;
break;
}
}
}
return nullptr;
}
int main() {
sigset_t set;
pthread_t thread;
sigemptyset(&set);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGINT);
// Блокируем сигналы во всех потоках
pthread_sigmask(SIG_BLOCK, &set, nullptr);
// Создаем поток для обработки сигналов
pthread_create(&thread, nullptr, signal_handler_thread, &set);
// Основная работа
while (true) {
sleep(1);
}
pthread_join(thread, nullptr);
return 0;
}
Сигналы, которые нельзя блокировать
SIGKILL и SIGSTOP никогда не могут быть заблокированы — это гарантирует, что ОС может принудительно остановить процесс.
Практические примеры
Защита критических операций:
void critical_operation() {
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGTERM);
// Блокируем сигналы
pthread_sigmask(SIG_BLOCK, &set, &oldset);
// Выполняем атомарную операцию
// Сигналы не прерывают выполнение
// Восстанавливаем маску
pthread_sigmask(SIG_SETMASK, &oldset, nullptr);
}
Когда использовать блокировку сигналов
- Критические секции — операции с базой данных, транзакции
- Многопоточность — предотвращение race conditions
- Безопасная очистка ресурсов — перед выходом из программы
- Синхронная обработка — вместо сложных обработчиков сигналов
- Инициализация — блокируем сигналы при запуске, разблокируем после
Возвращение к исходной маске
Всегда сохраняй старую маску и восстанавливай её:
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGTERM);
pthread_sigmask(SIG_BLOCK, &set, &oldset); // Сохраняем в oldset
// ... работа ...
pthread_sigmask(SIG_SETMASK, &oldset, nullptr); // Восстанавливаем
Блокировка сигналов — это фундаментальный инструмент для создания надежных и безопасных системных приложений.