Разработка приложений Linux и обработка сигналов

Разработка приложений Linux и обработка сигналов

В настоящее время у меня возникли проблемы с жалобами пользователей на завершение работы моего приложения. При некоторых (на первый взгляд произвольных) условиях и средах рабочего стола приложение не завершается, а настройки не сохраняются при перезагрузке. Я спрашивал в соответствующих каналах IRC, и в большинстве случаев мне говорят правильно обрабатывать сигналы. Я знаю о SIGINT для Ctrl-C в терминале и SIGTERM для «нормального» завершения. Но мне сказали, что SIGHUP тоже важен. Поэтому мой вопрос:

Какие сигналы мне необходимо обработать, чтобы создать хорошо работающее приложение?

решение1

https://en.wikipedia.org/wiki/Unix_signal

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

Вы должны только killс этим и ожидать, что ваши процессы будут killсовершеныСРОКи набор сигналов, которые может генерировать ваша среда (оболочка, драйвер терминала), если только вы не знаете, обрабатывает ли цель специальный сигнал, который вы хотите отправить.

Вы можете отсортировать основные сигналы по их действию.

Сигналы дампинга ядра

ABRT TRAP SYS BUS FPE ILL SEGV   XFSZ XCPU QUIT

генерируются либо вами (TRAP, ABRT, SYS) с помощью некоторых специализированных функций, либо при переходе в состояния серьезных ошибок (BUS, FPE,ILL, SEGV). QUITгенерируется пользователем на терминале, желающим получить дамп ядра ( C+\).

Возможно, вы захотите оставить их в их исходном состоянии.

Внезавершающие сигналы:

HUP INT PIPE TERM ALRM POLL PROF USR1 USR2

вы должны ожидать

HUP INT PIPE TERM

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

HUP -- when the terminal hungs up or you become a stopped orphaned process
INT -- when the user at the terminal interrupts you with C-c
PIPE -- when a PIPE or socket you write into closes, e.g. by
        exiting before you finished writing to it 
       (`yourprogram | (exit)` will give you a `PIPE` 
        if yourprogram attempts to write to its STDOUT) 
TERM -- when a process ends you a normal termination request

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

УБИЙСТВОиОСТАНАВЛИВАТЬСЯс этим ничего не поделаешь.

Вы можете захотеть перехватить терминал, сгенерированныйостанавливающие сигналы:

TTIN -- you read from the terminal and you're a backgrounded process
TTOU -- you write the terminal when you're a backgrounded process
        and the terminal is set to put you to sleep when you do
TSTP -- you're being put to sleep with `C-Z`

но в худшем случае они просто остановят ваш процесс, не нарушив вашего состояния.

Если вы не знаете, что вам нужно справиться с CHLD, CONT, или URG, вы этого не сделаете.

TL;DR:

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

В последнем случае вы можете просто заблокировать все необработанные сигналы, но помните, что маски блокировки сигналов наследуются между fork и вызовами execve, а игнорирование или блокировка сигналов, таких как ILL, если они возникли в результате выполнения вашего процесса и не были отправлены вам kill или sigqueue, приведет к неопределенному поведению.

Если вы хотите узнать больше, изучите man-страницы и стандарты. Сигналы — довольно большая тема в Unix, и их обработка может оказаться очень сложной.

решение2

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

Сказав это, вы можете взглянуть наsignals(7)чтобы получить список сигналов, которые могут вызвать завершение процесса:

SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1 и SIGUSR2.

Если ваше приложение проводит некоторое время подключенным к терминалу, обработка SIGINT— хорошая идея ( в конце концов, Ctrl+ Cдовольно известен). SIGTERMтакже хорошая идея, потому что он остается основным сигналом завершения и используется ядром (например, при завершении работы системы). SIGQUITвероятно, является «более жесткой» версией двух предыдущих, поскольку она вызывает дамп ядра. Когда вы получаете этот сигнал, ваша программа, вероятно, должна немедленно аварийно завершить работу. При получении любого из других сигналов ваша программа должна «убрать беспорядок и уйти» (сохранить настройки, завершить работу должным образом).

SIGILL, SIGFPE, SIGSEGVи SIGPIPE— сигналы, связанные с неправильным поведением программы.Ошибка сегментацииэто, вероятно, наиболее повторяющаяся ситуация, но в конечном итоге все эти случаи можно рассматривать как ошибки, и я не верю, что перехват сигнала — лучший способ исправить эти ситуации.

  • В большинстве случаев компиляторы не позволят SIGILLэтому случиться так просто (больше здесь), так что я думаю, вы можете отложить это в сторону.
  • Никогда не делите на ноль, и вы избежите большинства SIGFPEs. Кроме этого, просто убедитесь, что ваша программа хороша в математике и не сходит с ума от чисел.
  • SIGSEGVможет быть вызвано несколькими причинами (незаконный доступ к памяти, незаконное разыменование), и в этом случае я бы рекомендовал использовать функцию gdbдля наблюдения за вашей программой во время ее выполнения и отслеживания неприятной инструкции с помощью gdbстека.
  • Наконец, SIGPIPEв основном относится к потокам данных (чтение файлов, соединения, ...). Если какой-либо из потоков ввода-вывода вашей программы, вероятно, будет завершен во время выполнения, вам следует убедиться, что ваша программа проверяет свои дескрипторы перед их использованием и обрабатывает все события ввода-вывода, происходящие в ней (при использовании selectили pollнапример). gdbможет быть полезным и здесь.

SIGABRT, SIGALRM, SIGUSR1и SIGUSR2по сути являются пользовательскими сигналами. Если вы их получаете, скорее всего, вы действительно это сделали.

Это оставляет нас с SIGHUPи SIGKILL, которые могут быть не пойманы.

  • SIGHUPпроизойдет, например, если вы запустите процесс из терминала и закроете этот терминал после этого, не отсоединив процесс ( disownили используя nohup). Если ваша программа запускается из терминала и работает как демон, не забудьте отсоединить ее от оболочки, например, с помощью двойного разветвления. Этот конкретный сигнал менее важен, если ваша программа может запускаться из графического интерфейса или во время загрузки, когда управляющий терминал с меньшей вероятностью будет «закрыт».

  • SIGKILLкак вы знаете, является всемогущим из сигналов. Это способ ядра сказать "дерьмо!". Если вы получаете это, то вам следует сосредоточить свое внимание на том, что испускает сигнал, а не на том, как его обработать в вашей программе.

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

Если сигнал поступает «извне», то, вероятно, лучшим решением будет найти его источник.(если вы действительно не можете его найти,трассировка the kill(2)системный вызов может быть хорошим последним средством). Затем вы можете решить, хотите ли вы игнорировать его ( SIG_IGN), или принять его во внимание. Отключение программы-излучателя также может быть достойной альтернативой...

Связанный контент