Подтвердить что ты не робот

Простая обработка сигналов Linux

У меня есть программа, которая создает много потоков и запускается до тех пор, пока не отключится питание на встроенном компьютере, или пользователь не использует kill или ctrl c для завершения процесса.

Вот какой код и как выглядит main().

static int terminate = 0;  // does this need to be volatile?

static void sighandler(int signum) { terminate = 1; }

int main() {
  signal(SIGINT, sighandler);
  // ...
  // create objects, spawn threads + allocate dynamic memory
  // ...
  while (!terminate) sleep(2);
  // ...
  // clean up memory, close threads, etc.
  // ...
  signal(SIGINT, SIG_DFL);  // is this necessary?
}

Мне интересно несколько вещей:

  • Требуется ли обработка сигналов?
    Я прочитал в этой теме "Linux C catching kill для изящного завершения" , что, по-видимому, ОС будет обрабатывать очистку для меня. Поэтому могу ли я просто заменить обработчик сигнала только бесконечным циклом и позволить ОС изящно выйти из потоков, де-выделить память и т.д.?

  • Есть ли какие-либо другие сигналы, которые мне нужно беспокоиться относительно чистого завершения? Этот поток "Как SIGINT относится к другим сигналам терминалов?" , было полезно перечислить все сигналы, которые могут быть затронуты, но сколько фактически требуется для обработки?

  • Является ли переменная terminate в моем примере неустойчивой? Я видел много примеров, когда эта переменная является изменчивой, а другие - там, где это не так.

  • Я читал, что signal() теперь устарел, и использовать sigaction(). Есть ли действительно хорошие примеры, чтобы показать, как конвертировать из предыдущего вызова signal()? У меня возникли проблемы с новой структурой, которую я должен создать/передать и как все это сочетается.

  • Требуется ли второй вызов signal()?
    Есть ли что-то подобное, что мне нужно беспокоиться для sigaction()?

Чтобы быть ясным, все, что я пытаюсь выполнить, чтобы мой основной цикл выполнялся до тех пор, пока не будет ctrl c или отключено питание или что-то действительно плохое.

4b9b3361

Ответ 1

[Q-3] Используется ли переменная terminate в моем примере volatile? Я видел много примеров, когда эта переменная является изменчивой, а другие, где это не так.

Флаг terminate должен быть volatile sig_atomic_t:

Поскольку функции обработчика можно вызывать асинхронно. То есть, обработчик может быть вызван в любой момент программы, непредсказуемо. Если два сигнала поступают в течение очень короткого промежутка времени, один обработчик может работать в другом. И считается, что лучше объявить volatile sig_atomic_t, этот тип всегда доступен атомарно, избегая неопределенности в отношении прерывания доступа к переменной. volatile сообщает компилятору не оптимизировать и помещать его в регистр. (читайте: Атомный доступ к данным и обработка сигналов для подробного искупления).
Еще одна ссылка: 24.4.7 Атомный доступ к данным и обработка сигналов. Кроме того, стандарт C11 в 7.14.1.1-5 указывает, что доступ к объектам из volatile sig_atomic_t можно получить из обработчика сигнала (доступ к другим имеет поведение undefined).

[Q-4] Я читал, что signal() теперь устарел и используется sigaction(). Находятся есть действительно хорошие примеры, чтобы показать, как конвертировать из Предыдущий signal() вызов? У меня возникают проблемы с новой структурой, которая Мне нужно создать/передать и как все это сочетается.

Приведенный ниже пример (и ссылка в комментариях) может быть полезен:

// 1. Prepare struct 
struct sigaction sa;
sa.sa_handler =  sighandler;

// 2. To restart functions if interrupted by handler (as handlers called asynchronously)
sa.sa_flags = SA_RESTART; 

// 3. Set zero 
sigemptyset(&sa.sa_mask);

/* 3b. 
 // uncomment if you wants to block 
 // some signals while one is executing. 
sigaddset( &sa.sa_mask, SIGINT );
*/ 

// 4. Register signals 
sigaction( SIGINT, &sa, NULL );

ссылки:

[Q-5] Требуется ли второй вызов signal()? Есть ли что-то подобное, что мне нужно для sigaction()?

Почему вы устанавливаете его для действия по умолчанию до завершения программы для меня непонятно. Я думаю, что следующий параграф даст вам ответ:

Обработка сигналов

Вызов сигнала устанавливает обработку сигнала только для одного появления сигнала. Прежде чем вызывать функцию обработки сигналов, , библиотека сбрасывает сигнал, чтобы действие по умолчанию выполнялось, если снова появляется тот же сигнал. Сброс обработки сигналов помогает предотвратить бесконечный цикл, если, например, действие, выполняемое в обработчике сигнала, снова вызывает тот же сигнал. Если вы хотите, чтобы ваш обработчик использовался для сигнала каждый раз, когда он возникает, вы должны вызвать сигнал в обработчике, чтобы восстановить его. Вы должны быть осторожны в восстановлении обработки сигналов. Например, если вы постоянно восстанавливаете обработку SIGINT, вы можете потерять возможность прерывания и завершения вашей программы.

Функция signal() определяет обработчик только следующего принятого сигнала, после чего восстанавливается обработчик по умолчанию. Поэтому обработчик сигнала необходимо вызвать signal(), если программе необходимо продолжить обработку сигналов с помощью обработчика, отличного от стандартного.

Прочтите обсуждение для дальнейшего использования: Когда снова включить обработчики сигналов.

[Q-1a] Требуется ли обработка сигналов?

Да, Linux сделает для вас очистку. Например, если вы не закрываете файл или сокет, Linux выполнит очистку после завершения процесса. Но Linux может не понадобиться немедленно выполнить очистку, и может потребоваться некоторое время (возможно, чтобы высокая производительность системы или некоторые другие проблемы). Например, если вы не закрываете tcp-сокет, и программа завершает работу ядра, ядро ​​немедленно не закрывает сокет, чтобы гарантировать, что все данные были переданы, TCP гарантирует доставку, если это возможно.

[Q-1b] Следовательно, могу ли я просто заменить обработчик сигнала только бесконечным циклом и позволить ОС изящно выйти из потоков, де-выделить память и т.д.

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

[Q] Все, что я пытаюсь выполнить, чтобы мой основной цикл выполнялся до тех пор, пока не будет ctrl c, или питание не будет отключено, или что-то действительно плохое.

Нет, есть ограничение! Вы не можете поймать все сигналы. Некоторые сигналы не улавливаются, например. SIGKILL и SIGSTOP, и оба являются сигналами терминации. Цитирование:

- Макро: int SIGKILL

Сигнал SIGKILL используется для немедленного завершения программы. Он не может быть обработан или проигнорирован и поэтому всегда фатален. Также возможно заблокировать этот сигнал не.

Таким образом, вы не можете сделать программу, которая не может быть прервана (непрерывная программа)!

<суб > Я не уверен, но может быть, вы можете сделать что-то подобное в системах Windows: путем написания TSR (некоторая привязка к режиму ядра). Я помню из своего тезиса, что некоторые вирусы не могут быть прекращены даже из диспетчера задач, но я также считаю, что они обманывают пользователя по разрешениям администратора.
Суб >

Я надеюсь, что этот ответ поможет вам.

Ответ 2

Вместо использования sigaction вы можете использовать такую ​​функцию, как:

/*
 * New implementation of signal(2), using sigaction(2).
 * Taken from the book ``Advanced Programming in the UNIX Environment''
 * (first edition) by W. Richard Stevens.
 */
sighandler_t my_signal(int signo, sighandler_t func)
{
    struct sigaction nact, oact;

    nact.sa_handler = func;
    nact.sa_flags = 0;
    # ifdef SA_INTERRUPT
    nact.sa_flags |= SA_INTERRUPT;
    # endif
    sigemptyset(&nact.sa_mask);

    if (sigaction(signo, &nact, &oact) < 0)
        return SIG_ERR;

    return oact.sa_handler;
}

Ответ 3

1. Требуется ли обработка сигналов?

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

2. Существуют ли какие-либо другие сигналы, которые мне нужно учитывать в отношении чистого завершения?

  • Сначала взгляните на его страницу: Сигналы библиотеки GNU Сигналы оконечного устройства - это то, что вам нужно. Но взгляните на SIGUSR1 и SIGUSR2, даже если вы никогда не найдете их в каком-либо программном обеспечении, кроме отладочных целей.

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

3. Является ли переменная terminate в моем примере неустойчивой?

  • Абсолютно нет.

4. Я читал, что signal() теперь устарел, и использовать sigaction()

  • sigaction() является POSIX, тогда как сигнал является стандартом C.

  • signal() отлично работает для меня, но если вам нужен какой-то пример: Пример IBM

Ответ 4

Для этого не нужно использовать сигналы. Стандартное завершение не нужно ловить. Возможно, у вас есть причины его поймать, но это будет не к вашему приложению, а к чему-либо, что требуется O/S.

В терминах сигналов обычно вы должны использовать sigaction not signal в эти дни, он обходит проблему стандартизации.

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

Книга W. Richard Stevens "Расширенное программирование в среде UNIX" содержит хороший пример того, почему и как обрабатывать сигналы.

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

Ответ 5

Прежде всего - если вы не знаете, должны ли вы обрабатывать какие-либо сигналы, тогда вы, вероятно, этого не сделаете. Сигналы требуют обработки в некоторых конкретных случаях, таких как закрывающие сокеты, отправки некоторого сообщения другим подключенным процессам перед выходом или обработки сигнала SIGPIPE из write() (и, возможно, многих других).

Во-вторых - этот while (!terminate) sleep(2); не очень хорош - в худшем случае он может заставить пользователя (или даже систему) нетерпеливо ждать, когда ему будет ждать 2 с и отправить в вашу программу SIGKILL, с которой вы не можете справиться.

IMHO лучшим решением здесь будет с помощью signalfd и выберите, чтобы вы могли завершить свою программу, не ожидая двух секунд.