Предоставление / передача аргумента обработчику сигнала

Могу ли я предоставить / передать какие-либо аргументы обработчику сигналов?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */

Теперь обработчик выглядит так:

void signal_handler(int signo) {
    /* some code */
}

Если я хочу сделать что-то особенное, например удалить временные файлы, могу ли я предоставить эти файлы в качестве аргумента этому обработчику?

Изменить 0: Спасибо за ответы. Обычно мы избегаем / не рекомендуем использование глобальных переменных. И в этом случае, если у вас огромная программа, что-то может пойти не так в разных местах, и вам может потребоваться большая очистка. Почему API был разработан таким образом?


person hari    schedule 07.08.2011    source источник


Ответы (7)


У вас не может быть собственных данных, передаваемых обработчику сигнала в качестве параметров. Вместо этого вам придется хранить свои параметры в глобальных переменных. (И будьте очень, очень осторожны, если вам когда-нибудь понадобится изменить эти данные после установки обработчика сигналов).

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

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

person hmakholm left over Monica    schedule 07.08.2011
comment
Обработчики сигналов не зависят от потока. Изменение обработчика сигнала применяется ко всем потокам. - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
Правильно. Но все же имеет смысл хотеть, чтобы обработчик сигнала реагировал на то, для какого потока он обрабатывается, и это то, чего можно достичь с помощью TLS. - person hmakholm left over Monica; 07.08.2011
comment
На самом деле мне неясно, допустим ли доступ к TLS из обработчика сигнала. С glibc / NPTL он почти наверняка недействителен в целом, потому что сигнал может быть доставлен сразу после создания потока, но до того, как управление будет передано функции запуска, в то время как копия TLS потока все еще находится инициализирован. Если вы осторожны, чтобы заблокировать сигналы в это время, это может быть нормально, но все же это сомнительная практика ... - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
Кстати, даже без TLS есть один способ определить, из какого потока запущен обработчик сигнала, безопасным для асинхронных сигналов способом: &errno уникален для каждого потока! Однако я считаю, что это может быть небезопасно в glibc / NPTL по той же причине, которую я только что описал. Если это не так, я почти уверен, что это ошибка соответствия, но, вероятно, это достаточно редкая гонка, чтобы остаться незамеченной ... - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
@R .. Обработчики сигналов могут быть на основе потоков. POSIX предоставляет функции, которые можно использовать для отправки сигналов отдельным потокам в рамках одного процесса. - person Curious; 05.01.2016
comment
@Curious: Ваши два предложения не связаны. Да, вы можете отправить сигнал в конкретный поток, но обработчик находится в глобальном состоянии. Невозможно установить разные обработчики для каждого потока, за исключением реализации вашего собственного диспетчера в качестве основного обработчика сигналов, и это (1) сложно координировать между различными библиотеками, которым может потребоваться установить обработчик для каждого потока, и (2) технически невозможно без UB, потому что нет безопасного способа для обработчика сигналов определить, в каком потоке он работает. - person R.. GitHub STOP HELPING ICE; 05.01.2016
comment
Что всего за "Edit 0" в этом вопросе и в нескольких ответах? Был ли там пользователь с именем Edit 0, комментарии которого были удалены, или кто изменил свое имя пользователя? Во многих местах есть половина полезного диалога с пометкой Edit 0, и я бы очень хотел прочитать вторую половину ... - person jez; 19.12.2020

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

person R.. GitHub STOP HELPING ICE    schedule 07.08.2011
comment
Спасибо за ответ. Я не совсем понимаю, что вы здесь предлагаете. Не могли бы вы уточнить? Меня беспокоит, допустим, у меня есть 5 функций в программе, и в любой из них может произойти что-то не так, как я могу избежать объявления переменных global, чтобы позаботиться об очистке? - person hari; 07.08.2011
comment
Если вы не эксперт с сигналами, делать что-либо из обработчика сигналов очень опасно. Вместо этого вы можете просто использовать обработчик сигнала, чтобы сохранить флаг, что сигнал произошел, и проверить его из различных точек за пределами обработчика сигнала, или вы можете заблокировать сигналы и использовать sigpending или sigwait для опроса сигналов. - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
Вау, это для меня совершенно в новинку. Я видел, как код выполняет очистку, например, удаление временных файлов или очистку дочернего процесса до завершения основной программы. Также, когда вы говорите «проверить наличие флагов», как это может работать, если вы получаете SIGTERM? Разве ваша программа не завершится сразу, если вы не обработаете это в своем обработчике? Почему dangerous делать что-то внутри обработчика? - person hari; 07.08.2011
comment
Обработчик сигнала может прервать выполнение программы в любой момент. Это означает, что, если вы не позаботились о том, чтобы этого не произошло, он может работать с различными объектами данных, с которыми он хочет работать, в несовместимом состоянии. Например, указатели буфера внутри FILE могут быть обновлены только частично, или связанный список открытых FILEs может иметь частично вставленный или наполовину удаленный элемент, частично связанный с ним. Или внутренние malloc структуры данных могут иметь ссылку на наполовину освобожденную память ... Если обработчик сигналов вызывает функции, которые зависят от согласованности этого состояния, произойдут очень плохие вещи! - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
POSIX решает эти проблемы, определяя набор функций, которые безопасны для асинхронных сигналов, то есть их можно без проблем вызывать из обработчиков сигналов. Любая другая функция, которая является асинхронной-сигнальной-небезопасной, незаконна для вызова из обработчика сигнала, если вы не можете быть уверены, что обработчик сигнала не мог прервать любую другую асинхронную-сигнальную-небезопасную функцию. - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
Спасибо за информацию и объяснение. Я также прочитал немного больше об обработчиках сигналов. - person hari; 07.08.2011
comment
Между прочим, один классический любимый безопасный способ использования обработчиков сигналов - открыть канал для себя и сохранить дескрипторы файлов где-нибудь в глобальном масштабе. Обработчик сигнала может писать в канал (это безопасно), и вы можете ждать ввода из канала в основном цикле _1 _ / _ 2_ вместе с другими файловыми дескрипторами, от которых вы ожидаете ввода. - person R.. GitHub STOP HELPING ICE; 07.08.2011
comment
Ох, ладно. Не уверен, что понимаю, что вы хотите передать, но спасибо. Я посмотрю в Интернете и попытаюсь понять это. Ценю вашу помощь. - person hari; 07.08.2011
comment
Я только что разместил дополнительный вопрос: stackoverflow.com/questions/6971201/ - person hari; 07.08.2011
comment
@R .. в Linux есть signalfd (), который делает именно это. Все еще хорошая идея. Мало кто задумывается об использовании каналов для связи внутри процесса. Делает взаимодействие между потоками намного более простым и понятным. - person MauganRa; 02.03.2014

Это действительно старый вопрос, но я думаю, что могу показать вам хороший трюк, который помог бы решить вашу проблему. Нет необходимости использовать sigqueue или что-то еще.

Мне также не нравится использование глобальных переменных, поэтому мне пришлось найти умный способ в моем случае отправить void ptr (который позже вы можете использовать в соответствии с вашими потребностями).

На самом деле вы можете это сделать:

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc

Тогда ваш сигхандлер будет выглядеть так:

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !

Вы можете спросить: а как же тогда получить * ptr?

Вот как: При инициализации

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);

В вашей функции sighandler:

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}
person rak007    schedule 13.04.2017
comment
Хороший трюк, но, к сожалению, он не определен стандартом. См. 6.3.2.3 (8): if a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. - person eush77; 26.05.2017

Абсолютно. Вы можете передавать целые числа и указатели обработчикам сигналов, используя sigqueue () вместо обычного kill ().

http://man7.org/linux/man-pages/man2/sigqueue.2.html

person David Yeager    schedule 12.01.2016

Сохраните имена файлов в глобальной переменной, а затем получите к ней доступ из обработчика. Обратному вызову обработчика сигнала будет передан только один аргумент: идентификатор фактического сигнала, вызвавшего проблему (например, SIGINT, SIGTSTP)

Edit 0: «Должна быть веская причина не разрешать аргументы обработчику». ‹- Имеется вектор прерывания (в основном, набор адресов перехода к подпрограммам для каждого возможного сигнала). Учитывая способ, которым запускается прерывание, на основе вектора прерывания вызывается определенная функция. К сожалению, неясно, где будет вызвана память, связанная с переменной, и в зависимости от прерывания эта память может действительно быть повреждена. Есть способ обойти это, но тогда вы не можете использовать существующую инструкцию сборки int 0x80 (которую все еще используют некоторые системы)

person Foo Bah    schedule 07.08.2011

Я думаю, вам лучше использовать SA_SIGINFO в sa_flags, чтобы обработчик получил void signal_handler(int sig, siginfo_t *info, void *secret) в siginfo_t, вы можете предоставить свои параметры. Ty: СЧАСТЛИВЫЙ код

person fad    schedule 26.11.2020
comment
Вы устанавливаете какой-либо указатель или другие данные в объединении sigval, затем передаете это sigqueue() для постановки сигнала в реальном времени (определяемого пользователем), затем вы возвращаете то, что вы указали в обработчике сигнала, через объединение sigval в параметр siginfo_t. Однако вы не можете указать определяемые пользователем данные для сигнала не в реальном времени. Более того, об использовании sigqueue() уже упоминалось ранее ... - person Wad; 27.11.2020

Вы можете использовать обработчик сигнала, который является методом класса. Затем этот обработчик может получить доступ к данным-членам этого класса. Я не совсем уверен, что Python делает здесь под прикрытием вызова C signal (), но это должно быть изменение области видимости данных?

Я был поражен, что это работает, но это действительно так. Запустите это, а затем завершите процесс с другого терминала.

import os, signal, time

class someclass:
    def __init__(self):
        self.myvalue = "something initialized not globally defined"
        signal.signal(signal.SIGTERM, self.myHandler)
    def myHandler(self, s, f):
        # WTF u can do this?
        print "HEY I CAUGHT IT, AND CHECK THIS OUT", self.myvalue


print "Making an object"
a = someclass()

while 1:
    print "sleeping.  Kill me now."
    time.sleep(60)
person drizzo4shizzo    schedule 25.04.2012
comment
Вопрос был о C ... Хотя Python, скорее всего, где-то запоминает сигнал, чтобы обработать его позже как можно скорее (опасно делать что-то внутри обработчиков сигналов). Возможно использование глобальной переменной. - person MauganRa; 02.03.2014