Linux C перехватывает сигнал уничтожения для корректного завершения

У меня есть процесс, использующий сокеты, соединения с базой данных и тому подобное. По сути, это серверный процесс, осуществляющий ретрансляцию между данными датчиков и веб-интерфейсом, и поэтому важно обеспечить корректное завершение работы приложения в случае его остановки.

Как мне обрабатывать неожиданные исключения, такие как segfaults (по крайней мере, для отладки), а также сигналы kill, чтобы я мог закрыть любые соединения и остановить все запущенные потоки, чтобы процесс не оставил беспорядка из всего, что он использует?


person user623879    schedule 11.09.2011    source источник
comment
Имейте в виду, что продолжение запуска даже для очистки может быть опасным после ошибки сегментации.   -  person icktoofay    schedule 11.09.2011
comment
Также имейте в виду, что вы не можете поймать сигналы отключения.   -  person Gabe    schedule 11.09.2011
comment
Не уверен, какого беспорядка вы хотите избежать. Завершение потоков, закрытие файлов и освобождение памяти обычно выполняется ОС очень эффективно, поэтому в большинстве случаев простой выход может помочь. Есть ли что-то конкретное, о чем вы беспокоитесь?   -  person Soren    schedule 11.09.2011
comment
@gabe - лучше сказать, что ты не поймаешь SIGKILL, чтобы не запутать людей, которые никогда не использовали ничего, кроме команды kill из оболочки;)   -  person Brian Roach    schedule 11.09.2011
comment
@Soren - ловля SIGTERM и SIGINT - очень распространенная практика, позволяющая вам разумно выйти.   -  person Brian Roach    schedule 11.09.2011
comment
@Soren: Я не знаю проблем OP, но чтобы убедиться, что файлы всегда находятся в согласованном состоянии, транзакции базы данных откатываются и т. Д., Часто требуется очистка, которую ОС не может обработать, просто выйдя из вашего процесса.   -  person Gabe    schedule 11.09.2011
comment
возможный дубликат обработчика сигналов SIGKILL   -  person Ben Voigt    schedule 11.09.2011


Ответы (3)


Вы устанавливаете обработчики сигналов для перехвата сигналов - однако в 99% случаев вы просто хотите выйти и позволить ОС Linux позаботиться об очистке - она ​​с радостью закроет все файлы, сокеты, свободную память и завершит потоки.

Поэтому, если вы не хотите делать что-то конкретное, например, отправлять сообщение в сокеты, вам следует просто выйти из процесса и не пытаться поймать сигнал.

person Soren    schedule 11.09.2011
comment
Я думаю, вы правы ... в моем случае важнее надежность приложения, и делать сумасшедшие вещи с сигналами может быть плохо. Когда происходит segfault, если я получаю coredump, говорит ли он вам, где произошел segfault? - person user623879; 11.09.2011
comment
Стек вызовов сообщает, что произошла ошибка сегмента, при условии, что стек вызовов не был поврежден - этот вопрос здесь: stackoverflow.com/questions/105659/ рассказывает о том, как получить трассировку стека - person Soren; 11.09.2011
comment
Отличный совет. Исключением из этого правила является ситуация, когда вам необходимо очистить или заменить оборудование при завершении работы программы (в встроенных системах). - person Havok; 07.08.2014
comment
Старый вопрос, но мне все еще интересно, что вы имели в виду с 99%? - person Moritz Schmidt; 16.05.2018
comment
Я имею в виду, что если вы не очень опытный программист, который точно знает причину, по которой конкретное приложение должно улавливать сигнал, вам, вероятно, лучше вообще этого не делать. - person Soren; 17.05.2018
comment
Есть много ошибок памяти при завершении процесса, когда мы выполняем большую программу с помощью инструмента профиля или инструмента проверки памяти для обнаружения неочищенной памяти. Как при таком подходе исправить ошибки памяти? - person Dig The Code; 21.01.2019
comment
@DigTheCode - звучит как новый вопрос. Инструменты проверки памяти предназначены для того, чтобы показать вам утечки памяти, и очевидно, что вы не очистили память, которую вы выделяете, так что это то, что инструмент отмечает. Большинство отчетов инструментов содержат утечку памяти отдельно от не потерянной, но не освобожденной памяти - поэтому во многих случаях вам просто нужно правильно интерпретировать и понимать свои отчеты. - person Soren; 31.01.2019

Уловить сигналы сложно. Ты должен быть осторожен. Ваш первый шаг - использовать sigaction для установки обработчика сигналов для желаемых сигналов.

  • Выберите набор сигналов, на которые нужно реагировать, и выберите, что они значат для вашего процесса. Например, SIGTERM выход, SIGHUP перезапуск, SIGUSR1 перезагрузка конфигурации и т. Д.

  • Не пытайтесь реагировать на все сигналы и не пытайтесь «убрать» после сигнала, который указывает на ошибку в вашей программе. SIGKILL нельзя поймать. SIGSEGV, SIGBUS и им подобных не следует ловить, если у вас нет ОЧЕНЬ веской причины. Если вы хотите отлаживать, то увеличьте ulimit для дампа ядра - подключение отладчика к образу ядра намного эффективнее, чем все, что вы или я могли бы когда-либо кодировать. (Если вы все же попытаетесь очистить после SIGSEGV или чего-то подобного, знайте, что код очистки может вызвать дополнительные SIGSEGV, и все может быстро испортиться. Просто избегайте всего беспорядка и позвольте SIGSEGV завершить вашу программу.)

  • Как вы обрабатываете сигналы - непросто. Если в вашем приложении есть основной цикл (например, select или poll), то обработчик сигнала может просто установить флаг или записать байт в специальный канал, чтобы сигнализировать главному циклу о выходе. Вы также можете использовать siglongjmp, чтобы выйти из обработчика сигнала, но это ОЧЕНЬ сложно сделать правильно, и обычно это не то, что вам нужно.

Трудно что-то порекомендовать, не зная, как ваше приложение структурировано и что оно делает.

Также помните, что сам обработчик сигнала почти ничего не должен делать. Многие функции небезопасно вызывать из обработчиков сигналов.

person Dietrich Epp    schedule 11.09.2011

Иногда мне нравится получать обратную трассировку на SIGSEGV, часть отлова выглядит примерно так:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sig_handler(int);

int main() {
    signal(SIGSEGV, sig_handler);
    int *p = NULL;
    return *p;
}

void sig_handler(int sig) {
    switch (sig) {
    case SIGSEGV:
        fprintf(stderr, "give out a backtrace or something...\n");
        abort();
    default:
        fprintf(stderr, "wasn't expecting that!\n");
        abort();
    }
}

Вы должны быть очень осторожны с этими вещами, например убедитесь, что вы не можете вызвать другой сигнал.

person daniel    schedule 11.09.2011
comment
Разве не проще использовать ulimit для получения дампов ядра и трассировки стека таким образом? - person Dietrich Epp; 11.09.2011
comment
не знакомы с этим подходом, не могли бы вы уточнить? Я использую это только для отладки, кстати, т.е. я делаю ошибку и хочу сразу увидеть, откуда пришел сигнал. - person daniel; 11.09.2011
comment
Вам действительно не следует использовать signal. В течение многих лет рекомендуется не делать этого и вместо этого использовать sigaction. От man signal: Поведение signal () различается в разных версиях Unix, а также исторически менялось в разных версиях Linux. Избегайте его использования: используйте вместо него sigaction (2).. Я не голосую против ... но это близко. Не стоит никому рекомендовать его использовать. - person Brian Roach; 11.09.2011
comment
Команда ulimit позволяет сделать так, чтобы программы выгружали ядро ​​на SIGSEGV. Основной файл - это копия образа памяти вашей программы, и вы можете прикрепить к нему отладчик и копаться в памяти (получить трассировки стека, проверить переменные) или отправить основной файл разработчику, если это не ваша программа. - person Dietrich Epp; 11.09.2011