Постоянная обработка сигналов - PullRequest
1 голос
/ 27 ноября 2011

Я пытаюсь написать обработчик сигналов, чтобы перехватывать любое количество последовательных сигналов SIGINT и предотвращать выход из программы. Программа представляет собой простой файловый сервер. Обработчик устанавливает глобальный флаг, который приводит к завершению цикла while, принимающего новые соединения, вызов pthread_exit () гарантирует, что main разрешает текущим соединениям завершиться до выхода. Все идет как по маслу, когда я нажимаю ctrl-C один раз, но второй раз немедленно покидаю программу.

Сначала я попробовал с сигналом ():

signal(SIGINT, catch_sigint);

...

static void catch_sigint(int signo)
{
    ...
    signal(SIGINT, catch_sigint);
}

Я тоже пробовал, используя sigaction:

struct sigaction sigint_handler;
sigint_handler.sa_handler = catch_sigint;
sigemptyset(&sigint_handler.sa_mask);
sigint_handler.sa_flags = 0;
sigaction(SIGINT, &sigint_handler, NULL);

Не знаю, как «переустановить» этот код. Я просто продублировал этот код в обработчике, аналогичном обработчику с использованием метода signal ().

Ни одна из этих работ, как я ожидал.


Дополнительная информация:

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

int serverStop = 0;

...

int main()
{
   /* set up the server -- socket(), bind() etc. */

   struct sigaction sigint_hadler;
   sigint_handler.sa_handler = catch_sigint;
   sigint_handler.sa_flags = 0;
   sigemptyset(&sigint_handler.sa_mask);
   sigaction(SIGINT, &sigint_handler, NULL);

   /* signal(SIGINT, catch_sigint); */

   while(serverStop == 0)
   {
      /* accept new connections and pthread_create() for each */
   }
   pthread_exit(NULL);
}

...

static void catch_sigint(int signo)
{
   serverStop = 1;

   /* signal(SIGINT, catch_sigint) */
}

Я не думаю, что какой-либо другой код может быть уместным, но не стесняйтесь просить проработки

Ответы [ 3 ]

6 голосов
/ 28 ноября 2011

В Linux вам не нужно переустанавливать обработчик сигнала, используя либо signal (который реализует семантику BSD по умолчанию), либо sigaction.

, когдаЯ нажимаю Ctrl-C один раз, но второй раз немедленно покидаю программу.

Это не потому, что ваш обработчик получил сброс, а скорее всего потому, что ваш обработчик сигналов делает что-то, чего не должен.

Вот как я бы отладил эту проблему: запустите программу под GDB и

(gdb) catch syscall exit
(gdb) catch syscall exit_group
(gdb) run

Теперь немного подождите, пока программа начнет работать, и нажмите Control-C.Это даст вам (gdb) подсказку.Теперь продолжите программу, как будто она получила SIGINT: signal SIGINT (это вызовет ваш обработчик).Повторите последовательность «Control-C / сигнал SIGINT» снова.Если вы остановились в системном вызове exit или exit_group, посмотрите, откуда он поступает (с помощью команды GDB where).

Обновление:

Учитывая новый код, который выопубликовано, неясно, где именно вы звоните pthread_exit, чтобы «убедиться, что main позволяет текущим соединениям завершиться до выхода».Как написано, ваш основной поток выйдет из цикла на первом Control-C и продолжит вызов exit, который не будет ждать завершения других потоков.

Либо вы этого не сделалипокажите ваш фактический код, или «второй Control-C» - красная сельдь, и ваш первый Control-C уже вывез вас (без завершения работы в других потоках).

1 голос
/ 28 ноября 2011

ПРИМЕЧАНИЕ: это в значительной степени догадки.

Я почти уверен, что вызов pthread_exit в главном потоке - плохая идея.Если основной поток завершил работу, то ОС может попытаться отправить последующие сигналы другому потоку.

Я рекомендую вместо использования pthread_exit в основном потоке просто pthread_join () всех остальных потоков, затемвыйдите нормально.

Но также важно убедиться, что другие потоки не получают сигналы.Обычно это делается с помощью sigprocmask (или, вернее, pthread_sigmask, то же самое в Linux) для маскировки сигнала в рабочих потоках.Это гарантирует, что сигнал никогда не будет доставлен им.

Обратите внимание, что во избежание состязаний вы должны использовать pthread_sigmask в главном потоке непосредственно перед созданием дочернего потока, а затем снова установить маску сигнала в главном потоке.после этого.Это гарантирует отсутствие окна, даже маленького, в течение которого дочерний поток может получать нежелательные сигналы.

0 голосов
/ 27 ноября 2011

Я не уверен, что понял.Обработчик сигнала обычно не должен переустанавливать какой-либо обработчик сигнала (включая самого себя), потому что обработчик сигнала остается в рабочем состоянии до тех пор, пока не будет установлен другой.См. Также SA_NODEFER флаг sigaction , чтобы можно было перехватить сигнал во время его обработки.

Обработчик сигнала должен быть коротким.Смотрите мой ответ на этот вопрос .Обычно он устанавливает volatile sig_atomic_t переменную.

Что не работает?Не выполняйте сложную или длительную обработку внутри обработчиков сигналов.Пожалуйста, покажите свой код ...

...