Linux Kernel (Ядро линукса) (часть 2)


Сигналы

Сигналы были введены в первых Unix-системах для реализации взаимодействия между процессами в режиме пользователя. Кроме того, ядро с помощью сигналов уведомляет процессы о системных событиях. Сигналы существуют уже 30 лет и почти не изменились.
В первых разделах этой главы подробно рассматривается обработка сигналов ядром Linux; затем мы обсуждаем системные вызовы, позволяющие процессам обмениваться сигналами.

Роль сигналов

Сигнал — это очень короткое сообщение, которое может быть послано процессу или группе процессов. Информация, передаваемая процессу, как правило, сводится к номеру, идентифицирующему сигнал. В стандартных сигналах нет места аргументам, тексту сообщения или иной сопутствующей информации.
Для идентификации сигналов используется набор макросов, имена которых начинаются с префикса sig. В предыдущих главах мы уже упоминали их несколько раз. Например, в главе 3 речь шла о макросе sigchld. Этот макрос, расширяющийся в Linux в число 17, соответствует идентификатору сигнала, посылаемого процессу-родителю, когда его потомок приостанавливается или завершает работу. Макрос sigsegv, расширяющийся в число 11, Он соответствует идентификатору сигнала, посылаемого процессу, когда он делает недопустимую ссылку на ячейку памяти.

Сигнал служит двум основным целям:

-уведомляет процесс о том, что произошло определенное событие;
- заставляет процесс выполнить обработчик сигнала — функцию, включенную в код процесса.
Конечно, эти цели не являются взаимно исключающими, поскольку процесс часто должен реагировать на событие, выполняя некоторую процедуру.
содержит 31 сигнал, обрабатываемый в Linux 2.6 в архитектуре 80x86 (некоторые сигналы, такие как sigchld или sigstop, не зависят от архитектуры; другие же, например sigstkflt, определены только в конкретных архитектурах). Смысл действий, предпринимаемых по умолчанию, раскрывается в следующем разделе.

В дополнение к обычным сигналам, приведенным в этой таблице, стандарт POSIX определяет новый класс сигналов, так называемые сигналы реального времени. В Linux их номера находятся в диапазоне от 32 до 64. Они принципиально отличаются от обычных сигналов, потому что всегда ставятся в очередь так, что процесс принимает их все. Обычные сигналы того же типа в очередь не ставятся: если обычный сигнал посылается несколько раз подряд, процессу доставляется только один сигнал. Хотя ядро Linux не пользуется сигналами реального времени, оно полностью поддерживает стандарт POSIX при помощи специальных системных вызовов.

Ряд системных вызовов позволяет программистам посылать сигналы и определять реакцию процессов на получаемые сигналы. Эти системные вызовы а их поведение подробно описано в разд. Важной особенностью сигналов является то, что они могут быть посланы процессу в любой момент, и его состояние обычно непредсказуемо. Сигналы, посланные приостановленному процессу, должны быть сохранены ядром,
пока выполнение процесса не возобновится. Блокирование сигнала (описанное позже) означает, что доставка сигнала не должна производиться, пока блокирование не будет отменено, что усугубляет проблему сигналов, отправленных до того, как они могут быть доставлены.
Поэтому ядро различает две фазы передачи сигнала:
- генерирование сигнала— ядро обновляет некоторую структуру данных процесса-адресата для обозначения того факта, что сигнал послан;
- доставка сигнала— ядро заставляет процесс-адресат отреагировать на сигнал, т. е. изменить свое состояние, или вызвать указанный обработчик сигнала, или сделать и то и другое.
Каждый сгенерированный сигнал может быть доставлен не более одного раза. Сигналы являются невосстанавливаемыми ресурсами, т. е. после доставки сигнала вся информация о предыдущем сигнале, имеющаяся в дескрипторе процесса, утрачивается.
Сгенерированные, но еще не доставленные сигналы, называются висящими. В каждый момент времени у процесса может быть только один висящий сигнал данного типа. Следующие висящие сигналы того же типа, отправленные тому же процессу, не выстраиваются в очередь, а просто отбрасываются. С сигналами реального времени дело обстоит иначе: возможно наличие нескольких висящих сигналов одного типа.
Вообще говоря, сигнал может оставаться висящим непредсказуемо долго. Необходимо принимать во внимание следующие факторы:
- сигналы, как правило, доставляются только текущему процессу (то есть процессу current);
- сигналы определенного типа могут быть избирательно заблокированы процессом. В таком случае процесс не примет сигнал, пока не отменит блокировку;
- когда процесс выполняет обработчик сигнала, он обычно маскирует соответствующий сигнал, т. е. автоматически блокирует его, пока функция- обработчик не завершит выполнение. Таким образом, обработчик сигнала не может быть прерван другим поступлением обрабатываемого сигнала, и функция не должна быть реентерабельной.
Хотя работа с сигналами интуитивно понятна, ее реализация в ядре достаточно сложна.

Ядро должно:

- запомнить, какие сигналы заблокированы, какими процессами;
- при переключении из режима ядра в режим пользователя проверять, поступил ли сигнал для процесса. Это происходит почти при каждом прерывании от таймера (примерно каждую миллисекунду);
- определить, можно ли проигнорировать сигнал. Это происходит, когда удовлетворены все следующие условия:
• процесс-адресат не отслеживается другим процессом (флаг pt ptraced в поле ptrace дескриптора процесса равен О)1;
• сигнал не блокирован принимающим процессом;
• сигнал игнорируется принимающим процессом (либо потому что процесс игнорирует его явным образом, либо потому что процесс не изменил действие сигнала по умолчанию, которое заключается в игнорировании);
- обработать сигнал, для чего, возможно, потребуется переключить процесс на выполнение функции-обработчика в произвольной точке процесса, а затем восстановить контекст выполнения процесса по окончании работы функции.
В дополнение к сказанному, операционная система Linux должна учитывать
различную семантику сигналов в BSD и System V. Более того, она должна
соответствовать объемистым требованиям POSIX.
Действия, выполняемые при доставке сигнала
Процесс может отреагировать на сигнал трояко:
1. Явно проигнорировать сигнал.
2. Выполнить ассоциированное с сигналом действие по умолчанию Это действие, предопределенное ядром, зависит от типа сигнала и может быть следующим:
• завершить выполнение — процесс завершается (уничтожается);
• выполнить дамп— процесс завершается (уничтожается), и, если возможно, создается файл core с контекстом выполнения процесса. Этот файл может быть использован при отладке;
• игнорировать — сигнал игнорируется;
• остановить — процесс останавливается, т. е. переводится в состояние
task_stopped
• продолжить — если процесс был остановлен (task stopped), он переводится в состояние task running.
3. Принять сигнал, вызвав соответствующую функцию-обработчик сигнала.
Обратите внимание, что заблокировать сигнал— это не то же самое, что проигнорировать его. Пока сигнал блокирован, он не доставляется; доставка происходит только после снятия блокировки. Игнорируемый сигнал всегда доставляется, но дальнейшие действия не предпринимаются.

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

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

Предыдущая страница | 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без про...

ВведениеЕсли вы цените свое время, умеете считать деньги и знаете стоимость информации, то эта книга...

Linux Kernel (Ядро линукса) (ч...

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

Linux Kernel (Ядро линукса) (ч...

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно...

Linux Kernel (Ядро линукса) (ч...

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer...