Longjmp вне обработчика сигнала? - PullRequest
16 голосов
/ 07 сентября 2011

Из вопроса:

Является ли хорошей практикой программирования использование setjmp и longjmp на C?

В двух оставшихся комментариях сказано:

"Вы не можете выдать исключение в обработчике сигналов, но вы можете безопасно делать longjmp - если вы знаете, что делаете. - Дитрих Эпп 31 августа в 19:57 @Dietrich: +1на ваш комментарий. Это малоизвестный и полностью недооцененный факт. Есть ряд проблем, которые невозможно решить (неприятные условия гонки) без использования longjmp вне обработчиков сигналов. Асинхронное прерывание блокировки системных вызовов является классическим примером.. "

У меня сложилось впечатление, что обработчики сигналов вызываются ядром, когда оно сталкивается с исключительным условием (например, деление на 0).Кроме того, они вызываются только в том случае, если вы их специально зарегистрировали.

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

Продолжаем с этимдумал ... setjmp и longjmp, насколько я понимаю, предназначены для свертывания стека до предыдущей точки и состояния.Я не понимаю, как можно свернуть стек, когда вызывается обработчик сигнала, поскольку он вызывается из ядра как единичное обстоятельство, а не из вашего собственного кода.Что дальше в стеке из обработчика сигнала!?

Ответы [ 5 ]

16 голосов
/ 07 сентября 2011

То, как ядро ​​«вызывает» обработчик сигнала, прерывает поток, сохраняя маску сигнала и состояние процессора в структуре ucontext_t в стеке, который находится чуть дальше (ниже, при реализации с увеличением) стека прерванного кодауказатель и возобновление выполнения по адресу обработчика сигнала.Ядру не нужно отслеживать какое-либо состояние «этот процесс находится в обработчике сигнала»;это полностью следствие нового созданного кадра вызова.

Если прерванный поток находился в середине системного вызова, ядро ​​вернется из кода пространства ядра и отрегулирует адрес возврата, чтобы повторить системуcall (если для сигнала установлено SA_RESTART, а системный вызов перезапускаемый) или в коде возврата (если не перезапускается) EINTR.

Следует отметить, что longjmpАсинхр-сигнал небезопасным.Это означает, что он вызывает неопределенное поведение, если вы вызываете его из обработчика сигнала, если сигнал прервал другую асинхронную небезопасную функцию.Но до тех пор, пока прерванный код не использует библиотечные функции или использует библиотечные функции, помеченные как async-signal-safe, разрешается вызывать longjmp из обработчика сигнала.

Наконец, мой ответна основе POSIX, поскольку вопрос помечен unix.Если бы вопрос был только о чистом C, я подозреваю, что ответ несколько иной, но сигналы без POSIX все равно бесполезны ...

8 голосов
/ 07 сентября 2011

longjmp не выполняет обычное разматывание стека .Вместо этого указатель стека просто восстанавливается из контекста, сохраненного с помощью setjmp.

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

1 голос
/ 07 сентября 2011

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

0 голосов
/ 14 февраля 2019

стоит прочитать это: http://man7.org/linux/man-pages/man2/sigreturn.2.html в отношении того, как Linux обрабатывает вызов обработчика сигнала, и в этом случае, как он управляет выходом из обработчика сигнала, мое чтение этого предполагает, что выполняется longjmp () из обработчика сигнала (в результате ни один вызов sigreturn ()) не может быть в лучшем случае неопределенным ... также необходимо учитывать, в каком потоке (и, следовательно, в пользовательском стеке) вызывался setjmp () и в каком потоке (и, следовательно, в пользовательском стеке)) longjmp () в последующем также вызывается!

0 голосов
/ 07 сентября 2011

Вы не можете использовать longjmp для выхода из обработчика сигнала.

Причина этого в том, что setjmp сохраняет только ресурсы (регистры процесса) и т. Д., Которые определяет соглашение о вызовахэто должно быть сохранено через простой вызов функции.

Когда происходит прерывание, прерываемая функция может иметь гораздо большее состояние, и она не будет правильно восстановлена ​​longjmp.

...