Как чаще всего процессы обрабатывают сигнал term
Перейти к содержимому

Как чаще всего процессы обрабатывают сигнал term

  • автор:

IgorKa — Информационный ресурс

Немного обо всем и все о немногом, или практический опыт системного администратора.

Ноябрь 2009

Пн Вт Ср Чт Пт Сб Вс
« Окт Дек »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

Лекция №13 Сигналы в Linux

Calendar

11 ноября 2009, 11:36

Прошлая лекция была посвящена процессам в Linux. Сегодня мы поговорим о взаимодействии процессом между собой, а также о том как мы можем воздействовать на процессы. Сначала посмотрим как процессы могу взаимодействовать между собой. Мы уже писали в командной строке конструкции подобные этой: less /etc/group | grep user . В этом примере процесс less взаимодействует с процессом grep посредством механизма, который называется неименований канал или пайп (pipe — канал). Мы не будем вдаваться в подробности, просто запомните, что посредством символа |, который можно в данном случае называть “пайпом” информация (результат выполнения) процесса less идет на вход другого процесса — grep. Таким образом один процесс передал информацию другому процессу.

Еще один способ общения процессов — это именованные каналы. Изучение именованный каналов не входит в этот курс, но на практическом примере расскажу, что это. Именованный канал можно создать командой mkfifo:

igor@adm-ubuntu:~/linux$ mkfifo my_pipe
igor@adm-ubuntu:~/linux$ ls -l | grep my_pipe
prw-r–r– 1 igor igor 0 2009-11-09 17:59 my_pipe

Теперь в одной консоли выполните команду:

igor@adm-ubuntu:~/linux$ echo Hello > my_pipe

Как видите команда не завершает свою работу, а ждет. Зарегистрируйтесь еще в одной консоли и выполните команду:

igor@adm-ubuntu:~/linux$ cat my_pipe
Hello

Если вернуться на первую консоль, то вы увидите, что команда echo завершила свою работу. Таким образом через именованный канал my_pipe команда (процесс) echo передала информацию (слово Hello) процессу cat, который ее принял и вывел на экран.

Давайте теперь рассмотрим основной способ “общения” процессов — сигналы. Один процесс при помощи ядра может передать другому процессу специальное числовое значение сигнала. Процесс вызывает функцию передачи сигнала и передает необходимую информацию (код сигнала, PID процесса) ядру. Ядро передает сигнал процессу получателю и отслеживает как этот сигнал обрабатывается. Сигналы обозначаются цифрами или мнемоническими обозначениями. Перечень сигналов можно вывести командой kill -l.

Мнемонические имена которые вы видите (SIGTERM, SIGINT, SIGKILL) начинаются с приставки SIG. Имена в этом виде используются в языках программирования таких как С. В интерпретаторе bash используются или числа или мнемонические имена, но без приставки SIGTERM, INT, KILL.

Часть сигналов (INT, TERM) являются перехватываемыми. Это означает, что процесс при получении такого сигнала должен перейти на специальную подпрограмму, которая занимается обработкой сигнала. Если подпрограммы обработки нет (а ее написанием занимаются разработчики программы, которая выполняется в контексте процесса), то управление передается ядру, которое выполняет действия по умолчанию, описанные для каждого сигнала.

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

И есть третья группа сигналов, которые не блокируются и для которых не нужны обработчики. Примером такого сигнала является сигнал KILL. Этот сигнал уничтожает процесс, так как для него нет обработчиков в процессах, то он будет обрабатываться ядром по умолчанию и так как он не блокируемый, то действия будут выполнятся немедленно.

Пока мы говорили о том как процессы “общаются” между собой с помощью сигналов. Но мы (пользователи) также можем посылать сигналы процессам. Например, комбинация клавиш Ctrl+C посылает процессу сигнал INT, который прерывает выполнение процесса. Если вы наберете в терминале команду sleep 100, то команда не вернет управление терминалу пока не завершится. Прервать выполнение этой команды можно нажав комбинацию клавиш Ctrl+C.

В чем же отличия между похожими сигналами INT, TERM, KILL (а также QUIT и HUP)? Несмотря на похожесть отличия есть:

Сигнал KILL не блокируется и не перехватывается и ведет к немедленному завершению процесса.
Сигнал INT в отличии от KILL является блокируемым сигналом и перехватываемым.
Сигнал TERM также является перехватываемым и блокируемым и предназначен для корректного (предпочтительного) завершения работы процесса.
Сигнал QUIT — похож на TERM, но позволяет сохранить дамп памяти.
Сигнал HUP — сейчас этот сигнал чаще всего интерпретируется процессами как “прочесть конфигурационные файлы”.

Рассмотрим два сигнала: STOP и CONT. Сигнал STOP останавливает процесс, то есть процесс переходит в состояние “остановленный“. В таком состоянии процесс будет до тех пор пока снова не получит сигнал. Если будет получен сигнал CONT, то процесс возобновит свою работу с того момента как он был остановлен. Практический пример:

Наберите в терминале команду sleep 1000 &.

Затем проверьте, что процесс находится в состоянии ожидания, о чем нам говорит буква S в столбце STAT:

igor@ubuntu:~$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 S 0:00 sleep 1000

Теперь пошлем процессу сигнал STOP. Для этого используем команду kill (kill -название процесса PID процесса):

igor@ubuntu:~$ kill -STOP 6301
[1]+ Stopped sleep 1000

Проверяем статус процесса:

igor@ubuntu:~$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 T 0:00 sleep 1000

Видим, что процесс действительно находится в состоянии “остановленный” (символ T в столбце STAT).

Теперь отправим процессу сигнал продолжения работы (CONT) и проверим состояние:

igor@ubuntu:~$ kill -CONT 6301
igor@ubuntu:~$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 S 0:00 sleep 1000

Если необходимо корректно завершить процесс, то необходимо послать ему сигнал TERM:

igor@ubuntu:~$ kill -TERM 6301
igor@ubuntu:~$ ps x | grep [s]leep
[1]+ Terminated sleep 1000
igor@ubuntu:~$ ps x | grep [s]leep

Если сразу же после посылки сигнала TERM выполнить команду ps x | grep [s]leep, то можно успеть увидеть сообщение о том, что процесс завершает работу, в при следующей попытке вывести информацию о нашем процессе sleep мы уже ничего не увидим — процесс прекратил существование. Команда kill без указания сигнала, по умолчанию передает процессу именно сигнал TERM. Поэтому можно было написать просто kill 6301.

Если необходимо срочно завершить процесс, или процесс не завершается по сигналу TERM, то тогда необходимо послать процессу сигнал KILL:

igor@ubuntu:~$ sleep 1000 &
[1] 6348
igor@ubuntu:~$ kill -KILL 6348
igor@ubuntu:~$ ps x | grep [s]leep
[1]+ Killed sleep 1000

Если необходимо послать один и тот же сигнал нескольким процессам, то можно перечислить их через пробел: kill -TERM 2345 3456 4567.

Команда kill довольно ограничена в возможностях и не позволяет выполнять более сложные действия. Поэтому рассмотрим еще одну команду — killall. Основное преимущество этой команды, то что она умеет посылать сигналы всем процессам с одинаковым именем или всем процессам одного пользователя. Запустите несколько раз подряд команду sleep 1000 &:

igor@ubuntu:~$ ps x | grep [s]leep
6460 pts/1 S 0:00 sleep 1000
6461 pts/1 S 0:00 sleep 1000
6462 pts/1 S 0:00 sleep 1000
6463 pts/1 S 0:00 sleep 1000
6464 pts/1 S 0:00 sleep 1000
6465 pts/1 S 0:00 sleep 1000
6466 pts/1 S 0:00 sleep 1000

Теперь, чтобы завершить все процессы с именем sleep, достаточно набрать команду killall sleep:

igor@ubuntu:~$ killall sleep
[1] Terminated sleep 1000
[2] Terminated sleep 1000
[3] Terminated sleep 1000
[4] Terminated sleep 1000
[6]- Terminated sleep 1000
[7]+ Terminated sleep 1000
[5]+ Terminated sleep 1000

Выполните команду sleep 1000 & еще несколько раз, а затем зарегистрируйтесь в другой консоли от имени другого пользователя (например, test) и также от его имени выполните команду sleep 1000 &. Теперь вернитесь в свою консоль и просмотрите процессы sleep всех пользователей:

igor@ubuntu:~$ ps aux | grep [s]leep
igor 6540 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
igor 6541 0.0 0.0 2952 632 pts/1 S 22:30 0:00 sleep 1000
igor 6542 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
test 6543 0.0 0.0 2952 632 pts/3 S 22:30 0:00 sleep 1000
test 6544 0.0 0.0 2952 628 pts/3 S 22:30 0:00 sleep 1000
test 6545 0.0 0.0 2952 628 pts/3 S 22:30 0:00 sleep 1000
test 6546 0.0 0.0 2952 632 pts/3 S 22:30 0:00 sleep 1000

Теперь для того, чтобы удалить процессы только пользователя test необходимо выполнить (от имени пользователя root) команду killall -u test:

igor@ubuntu:~$ sudo killall -u test
igor@ubuntu:~$ ps aux | grep [s]leep
igor 6540 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
igor 6541 0.0 0.0 2952 632 pts/1 S 22:30 0:00 sleep 1000
igor 6542 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000

Команда выше удалит не только процессы sleep, но вообще все процессы пользователя test. Если необходимо удалить конкретно процессы sleep, то тогда команду нужно было записать так: killall -u test sleep.

Если запустить команду killall c ключом -i, то перед посылкой сигнала будет запрашиваться подтверждение:

igor@ubuntu:~$ killall -i sleep
Прибить sleep(6540) ? (y/N) n
Прибить sleep(6541) ? (y/N) n
Прибить sleep(6542) ? (y/N) n

Следующая лекция будет завершающей по теме процессов и сигналов Linux. Мы поговорим о заданиях (jobs), командах jobs, fg, bg, strong>nohup, и top.

8.4.4. Сигналы и команда kill

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

Сигналы принято обозначать номерами или символическими именами. Все имена начинаются на SIG, но эту приставку иногда опускают: например, сигнал с номером 1 обозначают или как SIGHUP, или просто как HUP.

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

Можно заставить процесс игнорировать или блокировать некоторые сигналы. Игнорируемый сигнал просто отбрасывается процессом и не оказывает на него никакого влияния. Блокированный сигнал ставится в очередь на выдачу, но ядро не требует от процесса никаких действий до разблокирования сигнала. После разблокирования сигнала программа его обработки вызывается только один раз, даже если в течение периода блокировки данный сигнал поступал несколько раз.

В табл. 8.1. приведены некоторые из часто встречающихся сигналов.

Таблица 8.1. Сигналы

N Имя Описание Можно перехватывать Можно блокировать Комбинация клавиш 1 HUP Hangup. Отбой Да Да 2 INT Interrupt. В случае выполнения простых команд вызывает прекращение выполнения, в интерактивных программах — прекращение активного процесса Да Да ‹Ctrl›+‹C› или ‹Del› 3 QUIT Как правило, сильнее сигнала Interrupt Да Да ‹Ctrl›+‹› 4 ILL Illegal Instruction. Центральный процессор столкнулся с незнакомой командой (в большинстве случаев это означает, что допущена программная ошибка). Сигнал отправляется программе, в которой возникла проблема Да Да 8 FPE Floating Point Exception. Вычислительная ошибка, например, деление на ноль Да Да 9 KILL Всегда прекращает выполнение процесса Нет Нет 11 SEGV Segmentation Violation. Доступ к недозволенной области памяти Да Да 13 PIPE Была предпринята попытка передачи данных с помощью конвейера или очереди FIFO, однако не существует процесса, способного принять эти данные Да Да 15 TERM Software Termination. Требование закончить процесс (программное завершение) Да Да 17 CHLD Изменение статуса порожденного процесса Да Да 18 CONT Продолжение выполнения приостановленного процесса Да Да 19 STOP Приостановка выполнения процесса Нет Нет 20 TSTR Сигнал останова, генерируемый клавиатурой. Переводит процесс в фоновый режим Да Да ‹Ctrl›+‹Z›

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

Для посылки сигнала процессу (или группе процессов) можно воспользоваться командой kill в следующем формате:

[user]$ kill [-сигн] PID [PID..]

где сигн — это номер сигнала, причем если указание сигнала опущено, то посылается сигнал 15 (TERM — программное завершение процесса). Чаще всего используется сигнал 9 (KILL), с помощью которого суперпользователь может завершить любой процесс. Но сигнал этот очень «грубый», если можно так выразиться, поэтому его использование может привести к нарушению порядка в системе. Поэтому в большинстве случаев рекомендуется использовать сигналы TERM или QUIT, которые завершают процесс более «мягко».

Естественно, что наиболее часто команду kill вынужден применять суперпользователь. Он должен использовать ее для уничтожения процессов-зомби, зависших процессов (они показываются в листинге команды ps как ‹exiting›), процессов, которые занимают слишком много процессорного времени или слишком большой объем памяти и т. д. Особый случай — процессы, запущенные злоумышленником. Но обсуждение этого особого случая выходит за рамки данной книги.

Читайте также

Сигналы

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

Сигналы

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

7.2 СИГНАЛЫ

7.2 СИГНАЛЫ Сигналы сообщают процессам о возникновении асинхронных событий. Посылка сигналов производится процессами — друг другу, с помощью функции kill, — или ядром. В версии V (вторая редакция) системы UNIX существуют 19 различных сигналов, которые можно классифицировать

5.8.2. Сигналы

5.8.2. Сигналы Демон syslogd реагирует на следующие сигналы: SYGTERM, SIGINT, SIGQUIT, SIGHUP, SIGUSR1, SIGCHLD. Реакция демона на сигналы описана в табл. 5.8.Реакция демона на сигналы Таблица 5.8 Сигнал Реакция SIGTERM Завершает работу демона SIGINT, SIGQUIT Завершает работу демона, если выключена отладка

3.3.2. Сигналы

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

27.3.10. Сигналы и сокеты

27.3.10. Сигналы и сокеты С сокетами связаны три сигнала:? SIGIO — сокет готов к вводу/выводу. Сигнал посылается процессу, который связан с сокетом;? SIGURG — сокет получил экспресс-данные (мы их использовать не будем, поэтому особо останавливаться на них нет смысла);? SIGPIPE — запись

Завершение процесса с помощью команды KILL

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

7.2.6.2. Сигналы

7.2.6.2. Сигналы Самый простой и грубый способ сообщения между двумя процессами на одной машине заключается в том, что один из них отправляет другому какой-либо сигнал (signal). Сигналы в операционной системе Unix представляют собой форму программного прерывания. Каждый сигнал

7.2.6.2. Сигналы

7.2.6.2. Сигналы Самый простой и грубый способ сообщения между двумя процессами на одной машине заключается в том, что один из них отправляет другому какой-либо сигнал (signal). Сигналы в операционной системе Unix представляют собой форму программного прерывания. Каждый сигнал

3.3. Сигналы

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

Пример 11-23. Сценарий, завершающий себя сам с помощью команды kill

Пример 11-23. Сценарий, завершающий себя сам с помощью команды kill #!/bin/bash# self-destruct.shkill $$ # Сценарий завершает себя сам. # Надеюсь вы еще не забыли, что «$$» — это PID сценария.echo «Эта строка никогда не будет выведена.»# Вместо него на stdout будет выведено сообщение «Terminated».exit 0# Какой

26.2. Сигналы

26.2. Сигналы Сигнал относится к типу сообщений, которые пересылаются из системы для информирования команды или сценария о совершении какого?либо события. Обычно речь идет об ошибках, связанных с функционированием памяти, о проблемах с доступом к информации или об

Сигналы, группы, сеансы

Сигналы в Unix указывают ядру, что надо прервать нормальное планирование процесса и завершить/остановить его, или, вместо продолжения выполнения процесса с места остановки, выполнить функцию — обработчик сигнала, и лишь затем продолжить выполнение основного кода.

Сигналы, предназначенные процессу, создаются (отправляются) в нескольких ситуациях: при аппаратных сбоях, при срабатывании особого таймера, при обработке спецсимволов (Ctrl C, Ctrl Z) драйвером управляющего терминала, с помощью системного вызова kill(). В зависимости от причины, отправляются сигналы разных типов. Тип сигнала обозначается целым числом (номером). В Linux сигналы нумеруются от 1 до 64. Сигнал может быть отправлен отдельной нити процесса, процессу в целом или группе процессов.

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

Между отправкой сигнала и его доставкой проходит некоторое непредсказуемое время, поскольку, обычно, сигналы обрабатываются (доставляются) при выходе из системных вызовов или в тот момент, когда планировщик назначает процесс на выполнение. Исключением является доставка сигнала SIGKILL остановленным процессам. Для большинства сигналов можно явно приостановить доставку с помощью установки маски блокирования доставки sigprocmask() , и, в случае необходимости, удалить сигнал без обработки с помощью sigwait() .

Сигналы SIGKILL и SIGSTOP не могут быть заблокированы или проигнорированы и на них нельзя установить свой обработчик.

Действия по умолчанию:

  • SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU — остановка процесса
  • SIGCONT — запуск остановленного процесса
  • SIGCHLD — игнорируется
  • SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGSEGV — сохранение дампа памяти и завершение (Linux)
  • остальные — завершение процесса

Сигнал, маска, обработчик

В упрощенном виде структуру в ядре, обеспечивающую доставку сигналов процессу, можно представить как таблицу:

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

Примеры использования сигналов

SIGKILL, SIGTERM, SIGINT, SIGHUP — завершение процесса. SIGKILL — не может быть проигнорирован, остальные могут. SIGTERM — оповещение служб о завершении работы ОС, SIGINT — завершение программы по нажатию Ctrl C, SIGHUP — оповещение программ, запущенных через модемное соединение, об обрыве связи (в настоящее время практически не используется).

SIGILL, SIGFPE, SIGBUS, SIGSEGV — аппаратный сбой. SIGILL — недопустимая инструкция CPU, SIGFPE — ошибка вычислений с плавающей точкой (деление на ноль), SIGBUS — физический сбой памяти, SIGSEGV — попытка доступа к несуществующим (защищенным) адресам памяти.

SIGSTOP, SIGCONT — приостановка и продолжение выполнения процесса

SIGPIPE — попытка записи в канал или сокет, у которого нет читателя

SIGCHLD — оповещение о завершении дочернего процесса.

Список сигналов

Особый сигнал с номером 0 фактически не доставляется, а используется в вызове kill() для проверки возможности доставки сигнала определённому процессу.

В Linux используется 64 сигнала. Список можно посмотреть в терминале командой kill -l

Posix.1-1990

Источник — man 7 signal

 Signal Value Action Comment ------------------------------------------------------------------------- SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at tty SIGTTIN 21,21,26 Stop tty input for background process SIGTTOU 22,22,27 Stop tty output for background process 

Отправка сигнала

int kill(pid_t pid, int signum);

Для отправка сигнала необходимо, чтобы uid или euid текущего процесса был равен 0 или совпадал с uid процесса получателя.

Получатель сигнала зависит от величины и знака параметра pid :

  • pid > 0 — сигнал отправляется конкретному процессу
  • pid == 0 — сигнал отправляется всем членам группы
  • pid == -1 — сигнал отправляется всем процессам кроме init (в Linux’е еще кроме себя)
  • pid < -1 - сигнал отправляется группе с номером -pid

Если signum == 0 — сигнал не посылается, но делается проверка прав на посылку сигнала и формируются код ответа и errno.

int sigqueue(pid_t pid, int sig, const union sigval value);

Аналогично kill() , но выполняется по правилам сигналов реального времени. Сигнал и связанная с ним дополнительная информация (целое или адрес) value помещаются в очередь сигналов (FIFO). Таким образом процессу можно отправить несколько однотипных сигналов с разными значениями value .

Отправка сигнала текущему процессу. Эквивалент kill(getpid(),signum);

Убирает из маски сигналов сигнала SIGABRT и отправляет его текущему процессу . Если сигнал перехватывается или игнорируется, то после возвращения из обработчика abort() завершает программу. Выхода из функции abort() не предусмотрено. Единственный способ продолжить выполнение — не выходить из обработчика сигнала.

Отправка сигнала SIGALRM себе через time секунд. Возвращает 0 если ранее alarm не был установлен или число секунд остававшихся до срабатывания предыдущего alarma. Таймер управляющий alarm’ами один. Соответственно установка нового alarm’а отменяет старый. Параметр time==0 позволяет получить оставшееся до alarm’а время без установки нового.

Исторический способ обработки сигнала — signal

Установка реакции на сигнал через функцию signal() не до конца стандартизована и сохраняется для совместимости с историческими версиями Unix. Не рекомендуется к использованию. В стандарте POSIX signal() заменен на вызов sigaction(), сохраняющий для совместимости эмуляцию поведения signal().

sighandler — адрес функции обработчика void sighandler(int) или один из двух макросов: SIG_DFL (обработчик по умолчанию) или SIG_IGN (игнорирование сигнала).

signal(. ) возвращает предыдущее значение обработчика или SIG_ERR в случае ошибки.

В Linux и SysV при вызове sighandler обработчик сбрасывается в SIG_DFL и возможна доставка нового сигнала во время работы sighandler . Такое поведение заставляет первой строкой в sighandler восстанавливать себя в качестве обработчика сигнала, и, даже в этом случае, не гарантирует от вызова обработчика по умолчанию. В BSD системах сброс не происходит, доставка новых сигнала блокируется до выхода из sighandler .

#include void sighandler(int signum) < signal(signum,sighandler); . >main()

Реакция на сигнал — sigaction

Параметры для установки обработчика сигнала через sigaction()

struct sigaction < void (*sa_handler)(int); // Обработчик сигнала старого стиля void (*sa_sigaction)(int, siginfo_t *, void *); // Обработчик сигнала нового стиля // Обработчик выбирается на основе флага SA_SIGINFO // в поле sa_flags sigset_t sa_mask; // Маска блокируемых сигналов int sa_flags; // Набор флагов // SA_RESETHAND - сброс обработчика на SIG_DFL после выхода из назначенного обработчика. // SA_RESTART - восстановление прерванных системных вызовов после выхода из обработчика. // SA_SIGINFO - вызов sa_sigaction вместо sa_handler void (*sa_restorer)(void); // Устаревшее поле >

Данные, передаваемые в обработчик сигнала sa_sigaction()

siginfo_t < int si_signo; // Номер сигнала int si_code; // Способ отправки сигнала или уточняющее значение // SI_USER сигнал отправлен через вызов kill() // SI_QUEUE сигнал отправлен через вызов sigqueue() // FPE_FLTDIV - уточнение для сигнала SIGFPE - деление на ноль // ILL_ILLOPC - уточнение для сигнала SIGILL - недопустимый опкод // . pid_t si_pid; // PID процесса отправителя (дочернего процесса при SIGCHLD) uid_t si_uid; // UID процесса отправителя int si_status; // Статус завершения дочернего процесса при SIGCHLD sigval_t si_value; // Значение, переданое через параметр value при вызове sigqueue() void * si_addr; // Адрес в памяти // SIGILL, SIGFPE - адрес сбойной инструкции // SIGSEGV, SIGBUS - адрес сбойной памяти >

В Linux структура siginfo_t содержит больше полей, но они служат для совместимости и не заполняются.

Код

#include void myhandler(int sig) < . >void myaction(int signum, siginfo_t * siginfo, void *code) < . >main()

Блокирование сигналов

Все функции блокирования сигналов работают с маской сигналов типа sigset_t. В Linux это 64 бита, т.е. два слова в 32х разрядной архитектуре или одно в 64х разрядной. Выставленный бит в маске сигналов означает, что доставка сигнала с соответствующим номером будет заблокирована. Сигналы SIGKILL и SIGSTOP заблокировать нельзя.

Манипулирование маской

#include int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum); int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); Изменить маску сигналов 

Параметр how определяет операцию над текущей маской. Значения SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK.

Проверка наличия заблокированных сигналов

int sigpending(sigset_t *set); // Получить список недоставленных из-за блокировки сигналов. int sigsuspend(const sigset_t *mask); //Установить новую маску и "уснуть" до получения и обработки разрешенного в ней сигнала. 

Очистка заблокированных сигналов

sigwait ожидает блокировки какого-либо из сигналов, указанных в маске, удаляет этот сигнал и возвращает его номер в параметр sig . Если реализация сигналов предусматривает очередь сигналов, то удаляется только один элемент из очереди.

int sigwait(const sigset_t *set, int *sig); 

Управляющий терминал, сеанс, группы

Управляющий терминал, сеанс, группы

Для организации диалоговой работы пользователей в Unix вводится понятие терминальной сессии. С точки зрения пользователя — это процесс работы с текстовым терминалом с момента ввода имени и пароля и до выхода из системы командой logout ( exit , нажатие ^D в пустой строке). Во время терминальной сессии может быть запущено несколько программ, которые будут параллельно выполнятся в фоновом режиме и между которыми можно переключаться в диалоговом режиме. После завершения терминальной сессии возможно принудительное завершение всех запущенных в ней фоновых процессов.

С точки зрения ядра — терминальная сессия — это группа процессов, имеющих один идентификатор сеанса sid. С идентификатором sid связан драйвер управляющего терминала, доступный всем членам сеанса как файл символьного устройства /dev/tty. Для каждого сеанса существует свой /dev/tty. Управляющий терминал взаимодействует с процессами сеанса с помощью отправки сигналов.

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

Группа процессов

Группа процессов — инструмент для доставки сигнала нескольким процессам, а также способ арбитража при доступе к терминалу. Идентификатор группы pgid равен pid создавшего её процесса — лидера группы. Процесс может переходить из группы в группу внутри одного сеанса.

#include int setpgid(pid_t pid, pid_t pgid); // включить процесс pid в группу pgid. // pid=0 означает текущий процесс, // pgid=0 означает pgid=pid текущего процесса // pid=pgid=0 - создание новой группы с pgid=pid текущего процесса // и переход в эту группу pid_t getpgid(pid_t pid); // получить номер группы процесса pid. // pid=0 - текущий процесс int setpgrp(void); // создание группы, эквивалент setpgid(0.0); pid_t getpgrp(void); // запрос текущей группы, эквивалент getpgid(0); 

Сеанс

Сеанс — средство для контроля путем посылки сигналов над несколькими группами процессов со стороны терминального драйвера. Как правило соответствует диалоговой пользовательской сессии. Идентификатор сеанса sid равняется идентификатору pid, создавшего его процесса — лидера сеанса. Одновременно с сеансом создаётся новая группа с pgid равным pid лидера сеанса. Поскольку переход группы из сеанса в сеанс невозможен, то создающий сеанс процесс не может быть лидером группы.

#include pid_t setsid(void); //Создание новой группы и нового сеанса. Текущий процесс не должен быть лидером группы. pid_t getsid(pid_t pid); //Возвращает номер сеанса для указанного процесса 

Создание собственного сеанса рекомендуется начать с fork, чтобы гарантировать, что процесс не является лидером группы.

if( fork() ) exit(0); setsid(); 

Фоновая группа сеанса

Процессы в фоновой группе выполняются до тех пор, пока не попытаются осуществить чтение или запись через файловый дескриптор управляющего терминала. В этот момент они получают сигнал SIGTTIN или SIGTTOU соответственно. Действие по умолчанию для данного сигнала — приостановка выполнения процесса.

Назначение фоновой группы:

#include pid_t tcgetpgrp(int fd); // получить pgid фоновой группы, связанной с управляющим терминалом, // на который ссылается файловый дескриптор fd int tcsetpgrp(int fd, pid_t pgrp); // назначить pgid фоновой группы терминалу, // на который ссылается файловый дескриптор fd 

Управляющий терминал

Некоторые сочетания клавиш позволяют посылать сигналы процессам сеанса:

  • ^C — SIGINT — завершение работы
  • ^Z — SIGTSTP — приостановка выполнения. bash отслеживает остановку дочерних процессов и вносит их в списки своих фоновых процессов. Остановленный процесс может быть продолжен командой fg n , где n — порядковый нрмер процесса в списке фоновых процессов

Открыть управляющий терминал сеанса

#include char name[L_ctermid]; int fd; ctermid(name); // если name=NULL, то используется внутренний буфер // ctermid возвращает указатель на буфер // L_ctermid - библиотечная константа fd = open(name, O_RDWR, 0); 

Сигналы

Предположим, что есть две программы. Одна программа должна передать другой большое количество данных. Передача данных будет происходить через файл типа FIFO. То есть одна программа открывает этот файл на запись и передает данные, другая должна открыть этот файл на чтение и получить данные. Проблема заключается в том, что вторая программа должна узнать, что пришло время открывать файл и читать данные.

Чтобы уведомить вторую программу, первая может послать ей сигнал. Сигнал — это число. Его можно представить в виде программного прерывания.

Мне сразу вспоминается очень известный анекдот про Петьку и Василия Ивановича:

Летят Петька и Василий Иванович на самолете.

Реакция программы на получаемый сигнал зависит от программиста, написавшего эту программу. Программист, написавший вторую программу из нашего примера, в документации к ней укажет, что если программе послать сигнал, например 10, то она откроет указанный FIFO-файл на чтение.

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

Чтобы получить список всех сигналов, которые поддерживаются системой, используйте программу kill с параметром –l:

$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 35) SIGRTMIN 36) SIGRTMIN+1 37) SIGRTMIN+2 38) SIGRTMIN+3 39) SIGRTMIN+4 40) SIGRTMIN+5 41) SIGRTMIN+6 42) SIGRTMIN+7 43) SIGRTMIN+8 44) SIGRTMIN+9 45) SIGRTMIN+10 46) SIGRTMIN+11 47) SIGRTMIN+12 48) SIGRTMIN+13 49) SIGRTMIN+14 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 $

Количество поддерживаемых сигналов зависит от типа системы. Даже в разных версиях Linux (имеются в виду версии ядра) может применяться разное количество сигналов. Но их никогда не бывает меньше 32.

Каждый сигнал имеет номер, полное и краткое имена. Например, сигнал 1 имеет полное имя SIGHUP, краткое имя сигнала — HUP.

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

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

Сигнал Описание сигнала Стандартная реакция программы
HUP(1) Сброс. Завершение работы. Для демонов — перечитать конфигурационный файл.
INT(2) Посылается, если нажата комбинация клавиш Ctrl+C. Завершение работы.
KILL(9) Безусловное завершение работы программы. Завершение работы.
TERM(15) Завершение работы программы. Завершение работы.
CONT(18) Продолжение выполнения приостановленной программы. Игнорируется.
STOP(19) Приостановление выполнения программы. Приостановление выполнения программы.

Сигнал TERM(15) посылается программе, если необходимо завершить ее выполнение. При получении этого сигнала программа выполняет все необходимые действия для завершения работы: закрывает соединения, открытые файлы, сохраняет данные. В общем, корректно завершает свою работу.

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

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

Внимание! Посылка сигнала KILL программе аналогична нажатию на кнопку reset, то есть программа завершает работу не корректно! Применять сигнал надо только в том случае, если программа не реагирует на посылку сигнала TERM.

Еще один сигнал, который обрабатывает система, а не программа, — STOP(19). При посылке этого сигнала программе ее выполнение будет приостановлено. Оперативная память, выделенная под программу, не освобождается, просто программа не будет ставится в очередь на выполнение процессору. Продолжить выполнение программы можно послав ей сигнал CONT(18).

Сигнал INT(2) посылается программе, если нажата комбинация клавиш Ctrl+C.

Сигнал HUP(1) посылается программе, если произошло отключение терминала. Действительно, в далекие времена рабочие места подключались через модемы, и при отключении связи программам посылался сигнал HUP. Поскольку ресурсы систем тогда были очень дорогими, система отключала все программы, подключенные к этому терминалу. На данный момент система ведет себя таким же образом, и при завершении работы логин шелл, всем программам посылается HUP, что бы они завершили свою работу. Но есть группа программ, которые в принципе не могут получить HUP — это процессы демоны. В случае демонов сигнал HUP используется по другому. Его посылают для того, что бы программа перечитала свои конфигурационные файлы.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *