Обработчики сигналов хитры в том, что они вызываются асинхронно, и поэтому существует только небольшой набор вызовов функций , которые можно безопасно вызывать из обработчика сигнала. В частности, выделение или освобождение памяти из обработчика сигнала - это нет-нет (как и вызов exit () !), Поэтому не делайте этого.
Однако, если вы хотите убедиться, что память освобождается (*), вы можете сделать это, попросив обработчик сигналов «сообщить» основному потоку вашей программы, что пора ей выходить. Затем основной поток может выйти из цикла обработки событий, освободить память и выполнить любую другую работу по очистке, которую он обычно выполняет перед выходом.
Итак, возникает вопрос, как обработчик сигнала может безопасно сказать основному потоку выполнить контролируемый / изящный выход?
Если основной поток выполняет цикл обработки событий, который выполняется по фиксированному расписанию (например, каждые очень много миллисекунд), это может быть так же просто, как объявление глобальной переменной (например, volatile bool pleaseQuitNow = false;
, которую основной поток проверяет на каждой итерации его цикла событий, и с помощью обработчика сигнала установите эту переменную в другое значение. Затем основной поток увидит измененную переменную на своей следующей итерации и ответит, выйдя из цикла событий.
Если цикл обработки событий основного потока основан на событиях, с другой стороны (например, он заблокирован внутри select () или poll () или аналогичный, и вызов выигран не возвращаться в течение некоторого неопределенного промежутка времени), то альтернативным способом пробуждения основного потока будет создание pipe () или socketpair () при запуске программы и попросите главный поток посмотреть один из двух файловых дескрипторов для получения статуса готовности к чтению. Затем, когда обработчик сигнала запускается, он может отправить () байт в другой файловый дескриптор, что заставит первый файловый дескриптор указывать состояние готовности к чтению. Основной поток может ответить на это состояние готовности к чтению, разорвав цикл обработки событий и выйдя изящно.
В дополнение к тому, что вы избегаете асинхронных небезопасных вызовов, преимущество этого способа заключается в том, что у вас есть только один путь выключения / очистки для тестирования / отладки / обслуживания вместо двух.
(*) Конечно, в любой современной ОС память все равно будет освобождена с помощью процедур очистки процессов ОС; но valgrind будет жаловаться на утечки памяти, поэтому лучше по возможности освободить память вручную, если только так, чтобы вы могли использовать valgrind для обнаружения «настоящих» утечек памяти без необходимости каждый раз сортировать кучу ложных срабатываний.