Вопрос проверяет понимание механизма сигналов (signals) в Unix-подобных системах для асинхронной обработки событий, что необходимо для написания надежных серверных приложений и демонов.
Сигналы в Unix-подобных системах (Linux, macOS) — это механизм уведомления процесса о наступлении определенного события. Они являются фундаментальным способом асинхронной обработки прерываний, поступающих от ядра, других процессов или пользователя. Сигнал прерывает нормальное выполнение программы, и если для него зарегистрирован обработчик, управление передается этой функции.
Когда событие, генерирующее сигнал, происходит (например, аппаратное исключение, таймер или команда от другого процесса), ядро устанавливает флаг в структуре данных целевого процесса. Позже, когда процесс собирается вернуться из режима ядра в пользовательское пространство, ядро проверяет наличие ожидающих сигналов. Если сигнал не заблокирован и для него назначен обработчик, ядро принудительно вызывает эту пользовательскую функцию, временно приостанавливая основную программу.
Вот простой пример программы, которая перехватывает SIGINT и SIGTERM для graceful shutdown:
#include
#include
#include
#include
volatile sig_atomic_t keep_running = 1;
void handle_signal(int sig) {
if (sig == SIGINT || sig == SIGTERM) {
printf("\nПолучен сигнал %d. Завершаем работу...\n", sig);
keep_running = 0;
}
}
int main() {
// Устанавливаем обработчики для SIGINT и SIGTERM
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
printf("PID: %d. Ожидаю сигналов (Ctrl+C для SIGINT).\n", getpid());
while (keep_running) {
// Имитация основной работы программы
sleep(1);
printf(".");
fflush(stdout);
}
printf("\nCleanup завершен. Выход.\n");
return 0;
}Обработчики сигналов выполняются в контексте прерывания, поэтому они должны быть максимально простыми и быстрыми. Большинство стандартных библиотечных функций (например, printf, malloc) не являются асинхронно-безопасными (async-signal-safe), и их вызов из обработчика может привести к неопределенному поведению. Безопасный подход — установить флаг типа sig_atomic_t внутри обработчика, а основная программа периодически проверяет этот флаг и выполняет тяжелую логику. Для более сложного управления сигналами рекомендуется использовать sigaction вместо устаревшего signal, так как он предоставляет больше контроля над поведением.
Вывод: Сигналы применяются для обработки асинхронных внешних событий в системном программировании, например, для graceful shutdown серверов, обработки ошибок (SIGSEGV) или межпроцессной синхронизации (SIGCHLD). Их стоит использовать, когда требуется реакция на события, генерируемые ядром или другими процессами, но важно помнить об их ограничениях и проектировать обработчики корректно.