Linux ctrl d какой сигнал

Обновлено: 07.07.2024

Сигналы в UNIX-системах являются одним из способов взаимодействия между процессами (англ. IPC, inter-process communication). Фактически, сигнал — это асинхронное уведомление процесса о каком-либо событии.

Каждый сигнал определяется кодом — небольшим целым числом (от 1).

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

Механизм сигналов был изобретён в 1970-х в Bell Labs.

Таблица сигналов

Каждый сигнал определяется кодом — небольшим целым числом (от 1).

Названия сигналов «SIG…» являются числовыми константами (макроопределениями C) со значениями, определяемыми в заголовочном файле signal.h. Числовые значения сигналов могут меняться от системы к системе, хотя основная их часть имеет в разных системах одни и те же значения.

Посылка сигналов

Безопасность

Процесс (или пользователь из оболочки) с эффективным UID, не равным 0 (UID суперпользователя), может посылать сигналы только процессам с тем же UID.

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

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

Сигнальная маска

Битмаски можно посмотреть в /proc/PID/status.

  • SigPnd (per-thread pending signals)
  • ShdPnd (process-wide pending signals; since Linux 2.6)
  • SigBlk (blocked signals)
  • SigIgn (ignored signals)
  • SigCgt (caught signals)

Опасность написания обработчика

Распространено мнение, что обработчик сигнала — это сложно и ничего в нем делать не надо. При написании обработчиков многие допускают ошибки.

Обработчик сигнала — это не какая-то неведомая сущность, которая может всё нестандартным образом испортить и из нее физически можно вызывать только малый набор функций. Обработчик сигнала — обычная функция, которая может выполнять все те же действия, что и другой код, которая работает в userspace, как и другой код. Единственные отличия:

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

Первая особенность практически никак не влияет, вторая особенность влечет за собой побочные эффекты:

  • вызывая некоторые функции из сигнала, которые работают в многопоточном режиме, мы можем попасть в deadlock, так как попали в обработчик сигнала из критической секции этой самой функции;
  • в однопоточном коде мы можем нарваться на структуры данных в неконсистентном состоянии, если попали в обработчик сигнала из кода, который работает с этими структурами;
  • некоторые библиотечные функции небезопасно вызывать, так как их выполнение тоже может прерываться обработчиком сигнала и они не reentrant. Есть гарантированный список POSIX-функций, которые безопасно вызывать в обработчике сигнала async-signal-safe (табличка в POSIX 2.4.3 Signal Actions). Функций вообще говоря не мало, например fork, exec, mkdir, open, write, connect, sigsuspend и прочие нетривиальные действия. Библиотечные функции sigprocmask, alarm, raise являются async-signal-safe. Такие функции, как malloc, free, запись в лог, обычно многопоточны, и здесь возможен deadlock.

Ещё надо понимать, что обработчик сигнала может быть вызван из состояния, когда уже всё нестандартным образом испорчено. Например, после «проезда по памяти», когда данные произвольным образом повреждены.

Установка обработчика сигнала в коде на C

Прерывание системных вызовов

Рестартуемые системные вызовы:

read(2), readv(2), write(2), writev(2), ioctl(2), open(2),wait(2), wait3(2), wait4(2), waitid(2), and waitpid,accept(2), connect(2), recv(2), recvfrom(2), recvmsg(2), send(2), sendto(2), and sendmsg(2) flock(2) and fcntl(2) mq_receive(3), mq_timedreceive(3), mq_send(3), and mq_timedsend(3) sem_wait(3) and sem_timedwait(3) futex(2)

Безопасно можно менять глобальные переменные типа

Пользовательские сигналы

SIGUSR1 и SIGUSR2 — пользовательские сигналы, которые могут быть использованы для межпроцессной синхронизации и управления.

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

Группа процессов (pgrp) — набор из одного и более процессов. Группе процессов можно послать сигнал одновременно.

Каждый процесс является членом некоторой группы процессов, которая идентифицируется своим process group ID (PGID). Это целое число типа pid_t, как и идентификатор процесса.

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

По соглашению, process group ID равен PID первого члена этой группы процессов, называемого лидером группы (process group leader). В группе процессов в любой момент не обязательно есть лидер: он мог уже завершиться, но важно, что группа начинается с лидера и его PID фиксируется в PGID. POSIX запрещает переиспользование PID, пока группа с таким идентификатором существует (т. е. лидер уже завершился, но какие-то процессы группы ещё нет). Поэтому новый произвольный процесс не может случайно стать лидером какой-то существующей группы.

Процесс может узнать ID своей группы при помощи системного вызова getpgrp(), или, что то же самое, getpgid(0). Можно получить process group ID для произвольного процесса p при помощи getpgid(p).

Можно использовать команду

для просмотра PPID (parent process ID), PID (process ID), PGID (process group ID) и SID (session ID).

Job control

  • В шеллах, которые не имеют возможности управления заданиями (job control), например ash, каждый дочерний процесс будет в той же сессии и в той же группе, как и сам шелл.
  • В более продвинутых шеллах, например bash, процессы конвейера, например

формируют отдельную группу.

Группы процессов являются низкоуровневым механизмом, который лежит в основе понятия задания (job). Те job ID, которые отображаются в шелле по команде jobs, создаются самим шеллом и искусственно начинаются с единицы.

Создание

Системный вызов setpgid() используется для того, чтобы установить process group ID для процесса, либо посредством присоединения процесса к уже существующей группе, либо посредством создания новой группы (процесс становится её лидером).

Процесс можно внеси в группу при помощи

Если pgid == pid или pgid == 0, то создаётся новая группа с лидером pid. Иначе процесс вносится в уже существующую группу pgid. Нулевой pid значит текущий процесс. Вызов setpgrp() эквивалентен вызову setpgid(0,0).

Ограничения setpgid()

Вызывающий процесс сам должен быть указанным pid'ом или его родителем, то есть можно переместить в другую группу себя или какого-либо своего потомка.

Обычный пример использования

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

Процесс — это экземпляр запущенной программы. Всякий раз, когда в терминале выполняется какая-нибудь команда (например, команда pwd ), система создает/запускает новый процесс.

Типы процессов

В Linux существует три основных типа процессов:

Как Linux идентифицирует процессы?

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

Родительские процессы — это процессы, которые во время своего выполнения создают другие процессы.

Дочерние процессы — эти процессы, создаваемые другими процессами во время своего выполнения.

Примечание: В любой момент времени в системе не существует двух процессов с одинаковым PID. Вновь создаваемому процессу может быть назначен ранее использованный свободный PID.

Состояния процесса в Linux


Рассмотрим основные состояния процесса:

Выполнение — процесс либо запущен (текущий процесс в системе), либо готов к запуску (ожидает передачи на выполнение процессору).

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

прерываемые ожидающие процессы — могут быть прерваны сигналами;

непрерываемые ожидающие процессы — процессы ожидают непосредственно на аппаратном уровне и не могут быть прерваны каким-либо событием/сигналом.

Завершен — процесс был остановлен, как правило, путем получения сигнала штатного завершения работы exit().

Как получить идентификатор (PID) процесса

Для отображения идентификатора нужного вам процесса можно использовать команду pidof, например:

$ pidof init
$ pidof bash
$ pidof systemd


Примечание: На вышеприведенном скриншоте вы можете видеть, что процессу init назначен PID=1 , а процессу systemd — PID=881 , хотя системой инициализации в Debian является именно systemd. Детально о том, почему возникла такая путаница, читайте здесь.

Чтобы вывести PID и PPID текущей оболочки, выполните:

$ echo $$
$ echo $PPID



Запуск интерактивного процесса в Linux

Как только вы выполните какую-нибудь команду или программу (например, firefox ), она создаст в системе соответствующий процесс. Вы можете запустить процесс переднего плана (он будет подключен к терминалу, ожидая пользовательского ввода) следующим образом:



Запуск фонового процесса в Linux

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

Кроме этого, в оболочку встроена утилита управления заданиями jobs, которая позволяет легко управлять несколькими процессами, переключая их между передним планом и фоновым исполнением. Также, с помощью jobs процессы могут быть сразу запущены в фоновом режиме.

Чтобы запустить процесс в фоновом режиме, используйте символ & после имени запускаемой программы. В этом случае процесс не будет принимать пользовательский ввод, пока не переместится на передний план:




Отслеживание активных процессов

Существует несколько различных инструментов для просмотра/перечисления запущенных в системе процессов. Двумя традиционными и хорошо известными из них являются команды ps и top:

Команда ps

Отображает информацию об активных процессах в системе, как показано на следующем скриншоте:


Для получения дополнительной информации о процессах, запущенных текущим пользователем, применяется опция -f :


Столбцы, присутствующие в выводе команды ps , имеют следующие значения:

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

PID — идентификатор процесса.

PPID — идентификатор родительского процесса.

C — загрузка CPU процессом.

STIME — время начала выполнения процесса.

TTY — тип терминала, связанного с процессом.

TIME — количество процессорного времени, потраченного на выполнение процесса.

CMD — команда, запустившая этот процесс.

Также можно отобразить информацию по конкретному процессу, используя команду ps -f [PID] , например:


Есть и другие опции, которые можно использовать вместе с командой ps :

-a — показывает информацию о процессах по всем пользователям;

-x — показывает информацию о процессах без терминалов;

-u — показывает дополнительную информацию о процессе по заданному UID или имени пользователя;

-e — отображение расширенной информации.

Если вы хотите вывести вообще всю информацию по всем процессам системы, то используйте команду ps –aux :


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

$ ps -aux --sort=%cpu


Если вы ходите выполнить сортировку по потреблению памяти (в порядке убывания), то добавьте к имени интересующего столбца знак минуса:

$ ps -aux --sort=-%mem


Еще один очень популярный пример использования команды ps — это объединение её и команды grep для поиска заданного процесса по его имени:

$ ps -aux | grep bash



Команда top

Команда top отображает информацию о запущенных процессах в режиме реального времени:


PID — идентификатор процесса.

USER — пользователь, которому принадлежит процесс.

PR — приоритет процесса на уровне ядра.

NI — приоритет выполнения процесса от -20 до 19 .

VIRT — общий объем (в килобайтах) виртуальной памяти (физическая память самого процесса; загруженные с диска файлы библиотек; память, совместно используемая с другими процессами и т.п.), используемой задачей в данный момент.

RES — текущий объем (в килобайтах) физической памяти процесса.

SHR — объем совместно используемой с другими процессами памяти.

%CPU — процент используемых ресурсов процессора.

%MEM — процент используемой памяти.

TIME+ — количество процессорного времени, потраченного на выполнение процесса.

COMMAND — имя процесса (команды).

< — процесс с высоким приоритетом;

N — процесс с низким приоритетом;

l — многопоточный процесс;

s — лидер сессии.

Примечание: Все процессы объединены в сессии. Процессы, принадлежащие к одной сессии, определяются общим идентификатором сессии — идентификатором процесса, который создал эту сессию. Лидер сессии — это процесс, идентификатор сессии которого совпадает с его идентификаторами процесса и группы процессов.

Команда glances

Команда glances — это относительно новый инструмент мониторинга системы с расширенными функциями:

Примечание: Если в вашей системе отсутствует данная утилита, то установить её можно с помощью следующих команд:

$ yum install -y glances

$ sudo apt-get update
$ sudo apt-get install glances

Управление процессами в Linux

Также в Linux присутствуют некоторые команды для управления процессами:

kill — посылает процессу сигнал завершения работы;

pkill — завершает процесс по его имени;

pgrep — ищет процесс по его имени (и, опционально, по имени запустившего его пользователя);

killall — завершает все активные процессы.

Ниже приведены несколько основных примеров их использования:

$ pgrep -u diego firefox
$ kill 6516
$ pgrep -u diego firefox
$ pgrep -u diego glances
$ pkill glances
$ pgrep -u diego glances



Отправка сигналов процессам

Основополагающим способом управления процессами в Linux является отправка им соответствующих сигналов. Для перечисления списка всех доступных сигналов, введите команду:


Большинство сигналов предназначены для внутреннего использования системой или для программистов, когда они пишут код. Ниже приведены наиболее полезные сигналы:

SIGHUP (1) — отправляется процессу, когда его управляющий терминал закрыт.

SIGINT (2) — отправляется процессу управляющим терминалом, когда пользователь прерывает процесс нажатием клавиш Ctrl+C.

SIGQUIT (3) — отправляется процессу, если пользователь посылает сигнал выхода Ctrl+D.

SIGKILL (9) — этот сигнал немедленно завершает (убивает) процесс, и процесс не будет выполнять никаких операций очистки за собой.

SIGTERM (15) — сигнал завершения программы (отправляется командой kill по умолчанию).

SIGTSTP (20) — отправляется процессу управляющим терминалом с запросом на остановку; инициируется пользователем нажатием клавиш Ctrl+Z.

Ниже приведены примеры команды kill для уничтожения приложения firefox с помощью PID, после его зависания:

$ kill -SIGKILL 2275

Чтобы убить приложение, используя его имя, применяются команды pkill или killall , например:

Изменение приоритета процесса

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

Узнать значение приоритета команды можно по выводу команды top (столбец NI):


Используйте команду nice , чтобы задать NI-значение для запускаемого процесса. Имейте в виду, что обычные пользователи могут задавать данный параметр в диапазоне от 0 до 20 тем процессам, которыми они владеют. Только пользователь root может использовать отрицательные значения приоритета.

Чем больше nice-значение, тем меньшим приоритетом будет обладать процесс. Например, вы можете задать приоритет для запускаемого процесса следующим образом:

$ nice -n 10 firefox

Чтобы изменить приоритет уже запущенного процесса, используйте команду renice следующим образом:

$ renice +8 5547
$ renice +8 1151

На данный момент это всё! Если у вас есть какие-либо вопросы или дополнительные идеи, вы можете поделиться ими с нами с помощью комментариев.

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

Ноябрь 2009
Пн Вт Ср Чт Пт Сб Вс
« Окт Дек »
1
2345678
9101112131415
16171819202122
23242526272829
30

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

Calendar

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

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

igor@adm-ubuntu:

/linux$ mkfifo my_pipe
igor@adm-ubuntu:

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

/linux$ echo Hello > my_pipe

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

/linux$ cat my_pipe
Hello

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

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

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

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

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

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

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

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

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

$ 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

Теперь отправим процессу сигнал продолжения работы (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, то тогда необходимо послать процессу сигнал KILL:

igor@ubuntu:

$ kill -KILL 6348
igor@ubuntu:

$ ps x | grep [s]leep
[1]+ Killed sleep 1000

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

$ 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

$ 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, то перед посылкой сигнала будет запрашиваться подтверждение:

$ 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.

Сигналы в 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

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(getpid(),signum);

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

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

Установка реакции на сигнал через функцию 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 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Сеанс

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

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

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

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

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