Сигналы - c99 против gnu99 - PullRequest
       20

Сигналы - c99 против gnu99

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

У меня есть следующий код.Когда я скомпилирую его с расширениями GNU (-std=gnu99), программа поймает 5 SIGINT перед завершением (что я ожидаю).При компиляции без него (-std=c99) заканчивается после второй (и выводит только одну строку).

Чего мне не хватает?

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

int int_stage = 0;
int got_signal = 0;

void sigint(int parameter)
{
  (void)parameter;
  got_signal = 1;
  int_stage++;
}

int main()
{
  signal(SIGINT,sigint);

  while(1)
  {
    if (got_signal)
    {
      got_signal = 0;
      puts("still alive");
      if (int_stage >= 5) exit(1);
    }
  }
  return 0;
}

Ответы [ 3 ]

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

Используйте sigaction(2) вместо signal(2).

Справочная страница Linux имеет это, в частности, в разделе о переносимости:

В исходных системах UNIX, когда обработчик, который был установлен с помощью signal (), был вызван доставка сигнала, расположение сигнала будет сброшено до SIG_DFL, и система сделала Не блокировать доставку дальнейших экземпляров сигнала. Система V также предоставляет эту семантику для сигнал(). Это было плохо, потому что сигнал мог быть доставлен снова, прежде чем у обработчика была возможность восстановить себя. Кроме того, быстрая доставка одного и того же сигнала может привести к рекурсивной вызовы обработчика.

BSD улучшил эту ситуацию, изменив семантику обработки сигналов (но, к сожалению, молча изменила семантику при установлении обработчика с сигналом ()). На BSD, когда сигнал вызывается обработчик, расположение сигнала не сбрасывается, и дальнейшие экземпляры сигнала заблокирован от доставки во время выполнения обработчика.

Ситуация в Linux выглядит следующим образом:

  • Системный вызов signal () ядра обеспечивает семантику System V.

  • По умолчанию в glibc 2 и новее функция-обертка signal () не вызывает систему ядра. вызов. Вместо этого он вызывает sigaction (2), используя флаги, которые предоставляют семантику BSD. Это поведение по умолчанию ior предоставляется до тех пор, пока определен макрос тестирования функции _BSD_SOURCE. По умолчанию _BSD_SOURCE определено; это также неявно определяется, если кто-то определяет _GNU_SOURCE, и, конечно, может быть правильно определено.
    В glibc 2 и более поздних версиях, если макрос тестирования функции _BSD_SOURCE не определен, то signal () предоставляет Семантика системы V. (Неявное определение по умолчанию _BSD_SOURCE не предоставляется, если вызывает gcc (1) в одном из его стандартных режимов (-std = xxx или -ansi) или определяет различные другие функции проверить макросы, такие как _POSIX_SOURCE, _XOPEN_SOURCE или _SVID_SOURCE; см. feature_test_macros (7).)

Используя std=gnu99, вы получаете семантику BSD. Используя -std=c99, вы получаете семантику System V. Таким образом, обработчик сигнала «переустанавливается» в одном случае (BSD), а расположение сигнала сбрасывается обратно в SIG_DFL в другом (System V).

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

Проблема в том, что сигнал также сбрасывает механизм обработки сигнала, вы должны сбросить sigint в качестве обработчика сигнала.Из руководства

В исходных системах UNIX, когда обработчик, который был установлен с помощью signal (), был вызван доставкой сигнала, расположение сигнала будет сброшено до SIG_DFL, иСистема не блокировала доставку дальнейших экземпляров сигнала.Система V также предоставляет эту семантику для signal ().Это было плохо, потому что сигнал мог быть доставлен снова, прежде чем у обработчика была возможность восстановить себя.Кроме того, быстрая доставка одного и того же сигнала может привести к рекурсивным вызовам обработчика.

Вот как это сделать со старым устаревшим вызовом signal ().Обратите внимание, что int_stage и got_signal должны быть sig_atomic_t.Вы также можете вызывать только асинхронные безопасные функции, посмотрите список здесь .

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

sig_atomic_t int_stage = 0;  
sig_atomic_t got_signal = 0;

void sigint(int parameter)
{
  (void)parameter;
  got_signal = 1;
  int_stage++;
}

int main()
{
   signal(SIGINT,sigint);

   while(1)
   {
      if (got_signal)
      {
       signal(SIGINT,sigint);
       got_signal = 0;
       puts("still alive");
       if (int_stage >= 5) exit(1);
    }
 }
 return 0;
}

Пожалуйста, рассмотрите возможность использования sigaction или sigwait.

Sigaction практическита же идея, но не чепуха с повторной инициализацией обработчика сигнала.Sigwait остановит ваш поток, пока не будет получен сигнал.Таким образом, для sigwait, вы можете вызвать любую функцию или иметь дело с любыми данными.Я могу показать вам пример кода, если вы хотите.

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

Я согласен с Итаном Стейнбергом - «Ожидание занята» - это так неправильно ...

Но проблема в том, что вы не можете сбросить обработчик сигнала. AFAIK, вы должны сделать это (снова вызвать «signal (SIGINT, sigint)») с любой версией C.

...