The design of the unix operating system by Maurice J
Вид материала | Реферат |
- Лекция 10. Файловые системы Unix, 116.79kb.
- Уровни рассмотрения, 314.07kb.
- Курс по операционным системам (на примере ос windows) Основан на учебном курсе Windows, 29.21kb.
- Выполнил ученик 11 «А» класса, 443.51kb.
- Ос лекция 1 (2-й семестр – временно), 101.4kb.
- Operating System, 7686.97kb.
- Unix-подобные операционные системы, характеристики, особенности, разновидности, 40.63kb.
- 1. ms sql server. Общие сведения, 66.03kb.
- Shanti ananda maurice, 89.84kb.
- Методические материалы, 3002.45kb.
* Сигналы, посылаемые процессу, который выполняется в режиме
задачи, например, сигнал тревоги (alarm), посылаемый по исте-
чении определенного периода времени, или произвольные сигна-
лы, которыми обмениваются процессы, использующие функцию
kill;
* Сигналы, связанные с терминальным взаимодействием, например,
с "зависанием" терминала (когда сигнал-носитель на терминаль-
ной линии прекращается по любой причине) или с нажатием кла-
виш "break" и "delete" на клавиатуре терминала;
* Сигналы, с помощью которых производится трассировка выполне-
ния процесса.
Условия применения сигналов каждой группы будут рассмотрены в
этой и последующих главах.
Концепция сигналов имеет несколько аспектов, связанных с тем,
каким образом ядро посылает сигнал процессу, каким образом про-
цесс обрабатывает сигнал и управляет реакцией на него. Посылая
сигнал процессу, ядро устанавливает в единицу разряд в поле сиг-
нала записи таблицы процессов, соответствующий типу сигнала. Если
процесс находится в состоянии приостанова с приоритетом, допуска-
ющим прерывания, ядро возобновит его выполнение. На этом роль от-
правителя сигнала (процесса или ядра) исчерпывается. Процесс мо-
жет запоминать сигналы различных типов, но не имеет возможности
запоминать количество получаемых сигналов каждого типа. Например,
если процесс получает сигнал о "зависании" или об удалении про-
цесса из системы, он устанавливает в единицу соответствующие раз-
ряды в поле сигналов таблицы процессов, но не может сказать,
сколько экземпляров сигнала каждого типа он получил.
Ядро проверяет получение сигнала, когда процесс собирается
перейти из режима ядра в режим задачи, а также когда он переходит
в состояние приостанова или выходит из этого состояния с доста-
точно низким приоритетом планирования (см. Рисунок 7.6). Ядро об-
рабатывает сигналы только тогда, когда процесс возвращается из
режима ядра в режим задачи. Таким образом, сигнал не оказывает
немедленного воздействия на поведение процесса, исполняемого в
режиме ядра. Если процесс исполняется в режиме задачи, а ядро тем
временем обрабатывает прерывание, послужившее поводом для посылки
процессу сигнала, ядро распознает и обработает сигнал по выходе
из прерывания. Таким образом, процесс не будет исполняться в ре-
жиме задачи, пока какие-то сигналы остаются необработанными.
На Рисунке 7.7 представлен алгоритм, с помощью которого ядро
определяет, получил ли процесс сигнал или нет. Условия, в которых
формируются сигналы типа "гибель потомка", будут рассмотрены поз-
же. Мы также увидим, что процесс может игнорировать отдельные
сигналы, если воспользуется функцией signal. В алгоритме issig
ядро просто гасит индикацию тех сигналов, на которые процесс не
желает обращать внимание, и привлекает внимание процесса ко всем
остальным сигналам.
Выполняется в
режиме задачи
--------┐
│ │ Проверка
│ 1 │ и
Вызов функ- │ │ - - обработка
ции, преры- LT------- - - сигналов
вание │ - --
Преры- ------┐ --------- │- -│- - -
вание, │ │ │ --------- L---┐ Возврат в
возврат│ │ │ │ Возврат │ режим задачи
из пре-│ │ │ │ │
рыва-│ v v │ Выполняет- │
--------┐ ния │ -------+┐ся в режи- -+------┐
│ │ L-->│ │ме ядра │ │
│ 9 │<-----------+ 2 +------------>│ 7 │
│ │ Выход │ │ Резервирует-│ │
L-------- LT------- ся L--------
Прекращение │ Зарезер-
существования │- - -│- - - - - - - - ┐ вирован
│ │- - - - - - - ┐ L - -┐
----------------- L------┐ Проверка
│ Приостанов Запуск │ L - - - сигналов
v │
При---------┐ --+-----┐ Готов к
ос- │ │ Возобновление │ │ запуску
та- │ 4 +----------------------->│ 3 │ в памяти
нов-│ │ │ │
лен L---T---- LT-------
в па- │ │
мяти │ │ │ │ Достаточно
│ │ │ │ памяти
│ │ │ L---┐
│ Вы- Вы- │ │ │
│ грузка грузка │ │ │ Создан
│ │ │За- -+------┐
│ │ │груз-│ │ fork
│ │ │ка │ 8 │<-----
│ │ │ │ │
│ │ │ LT-------
│ │ │ │
│ │ │ │ Недоста-
│ │ │ ----- точно
│ │ │ │ памяти
│ │ │ │ (только система
│ │ │ │ подкачки)
v v │ v
--------┐ ----+---┐
│ │ Возобновление │ │
│ 6 +----------------------->│ 5 │
│ │ │ │
L-------- L--------
Приостановлен, Готов к запуску,
выгружен выгружен
Рисунок 7.6. Диаграмма переходов процесса из состояние в сос-
тояние с указанием моментов проверки и обработки
сигналов
-------------------------------------------------------------┐
│ алгоритм issig /* проверка получения сигналов */ │
│ входная информация: отсутствует │
│ выходная информация: "истина", если процесс получил сигна- │
│ лы, которые его интересуют │
│ "ложь" - в противном случае │
│ { │
│ выполнить пока (поле в записи таблицы процессов, содер- │
│ жащее индикацию о получении сигнала, хранит ненулевое │
│ значение) │
│ { │
│ найти номер сигнала, посланного процессу; │
│ если (сигнал типа "гибель потомка") │
│ { │
│ если (сигналы данного типа игнорируются) │
│ освободить записи таблицы процессов, которые │
│ соответствуют потомкам, прекратившим существо-│
│ вание; │
│ в противном случае если (сигналы данного типа при-│
│ нимаются) │
│ возвратить (истину); │
│ } │
│ в противном случае если (сигнал не игнорируется) │
│ возвратить (истину); │
│ сбросить (погасить) сигнальный разряд, установленный │
│ в соответствующем поле таблицы процессов, хранящем │
│ индикацию получения сигнала; │
│ } │
│ возвратить (ложь); │
│ } │
L-------------------------------------------------------------
Рисунок 7.7. Алгоритм опознания сигналов
7.2.1 Обработка сигналов
Ядро обрабатывает сигналы в контексте того процесса, который
получает их, поэтому чтобы обработать сигналы, нужно запустить
процесс. Существует три способа обработки сигналов: процесс за-
вершается по получении сигнала, не обращает внимание на сигнал
или выполняет особую (пользовательскую) функцию по его получении.
Реакцией по умолчанию со стороны процесса, исполняемого в режиме
ядра, является вызов функции exit, однако с помощью функции
signal процесс может указать другие специальные действия, прини-
маемые по получении тех или иных сигналов.
Синтаксис вызова системной функции signal:
oldfunction = signal(signum,function);
где signum - номер сигнала, при получении которого будет выполне-
но действие, связанное с запуском пользовательской функции,
function - адрес функции, oldfunction - возвращаемое функцией
значение. Вместо адреса функции процесс может передавать вызывае-
мой процедуре signal числа 1 и 0: если function = 1, процесс бу-
дет игнорировать все последующие поступления сигнала с номером
signum (особый случай, связанный с игнорированием сигнала "гибель
потомка", рассматривается в разделе 7.4), если = 0 (значение по
умолчанию), процесс по получении сигнала в режиме ядра завершает-
ся. В пространстве процесса поддерживается массив полей для обра-
ботки сигналов, по одному полю на каждый определенный в системе
сигнал. В поле, соответствующем сигналу с указанным номером, ядро
сохраняет адрес пользовательской функции, вызываемой по получении
сигнала процессом. Способ обработки сигналов одного типа не вли-
яет на обработку сигналов других типов.
-------------------------------------------------------------┐
│ алгоритм psig /* обработка сигналов после проверки их │
│ существования */ │
│ входная информация: отсутствует │
│ выходная информация: отсутствует │
│ { │
│ выбрать номер сигнала из записи таблицы процессов; │
│ очистить поле с номером сигнала; │
│ если (пользователь ранее вызывал функцию signal, с по- │
│ мощью которой сделал указание игнорировать сигнал дан- │
│ ного типа) │
│ возвратить управление; │
│ если (пользователь указал функцию, которую нужно выпол- │
│ нить по получении сигнала) │
│ { │
│ из пространства процесса выбрать пользовательский │
│ виртуальный адрес функции обработки сигнала; │
│ /* следующий оператор имеет нежелательные побочные │
│ эффекты */ │
│ очистить поле в пространстве процесса, содержащее │
│ адрес функции обработки сигнала; │
│ внести изменения в пользовательский контекст: │
│ искусственно создать в стеке задачи запись, ими- │
│ тирующую обращение к функции обработки сигнала; │
│ внести изменения в системный контекст: │
│ записать адрес функции обработки сигнала в поле │
│ счетчика команд, принадлежащее сохраненному ре- │
│ гистровому контексту задачи; │
│ возвратить управление; │
│ } │
│ если (сигнал требует дампирования образа процесса в па- │
│ мяти) │
│ { │
│ создать в текущем каталоге файл с именем "core"; │
│ переписать в файл "core" содержимое пользовательско-│
│ го контекста; │
│ } │
│ немедленно запустить алгоритм exit; │
│ } │
L-------------------------------------------------------------
Рисунок 7.8. Алгоритм обработки сигналов
Обрабатывая сигнал (Рисунок 7.8), ядро определяет тип сигнала
и очищает (гасит) разряд в записи таблицы процессов, соответству-
ющий данному типу сигнала и установленный в момент получения сиг-
нала процессом. Если функции обработки сигнала присвоено значение
по умолчанию, ядро в отдельных случаях перед завершением процесса
сбрасывает на внешний носитель (дампирует) образ процесса в памя-
ти (см. упражнение 7.7). Дампирование удобно для программистов
тем, что позволяет установить причину завершения процесса и пос-
редством этого вести отладку программ. Ядро дампирует состояние
памяти при поступлении сигналов, которые сообщают о каких-нибудь
ошибках в выполнении процессов, как например, попытка исполнения
запрещенной команды или обращение к адресу, находящемуся за пре-
делами виртуального адресного пространства процесса. Ядро не дам-
пирует состояние памяти, если сигнал не связан с программной
ошибкой. Например, прерывание, вызванное нажатием клавиш
"delete" или "break" на терминале, имеет своим результатом посыл-
ку сигнала, который сообщает о том, что пользователь хочет раньше
времени завершить процесс, в то время как сигнал о "зависании"
является свидетельством нарушения связи с регистрационным терми-
налом. Эти сигналы не связаны с ошибками в протекании процесса.
Сигнал о выходе (quit), однако, вызывает сброс состояния памяти,
несмотря на то, что он возникает за пределами выполняемого про-
цесса. Этот сигнал, обычно вызываемый одновременным нажатием кла-
виш
ния памяти в любой момент после запуска процесса, что бывает
необходимо, если процесс попадает в бесконечный цикл выполнения
одних и тех же команд (зацикливается).
Если процесс получает сигнал, на который было решено не обра-
щать внимание, выполнение процесса продолжается так, словно сиг-
нала и не было. Поскольку ядро не сбрасывает значение соответс-
твующего поля, свидетельствующего о необходимости игнорирования
сигнала данного типа, то когда сигнал поступит вновь, процесс
опять не обратит на него внимание. Если процесс получает сигнал,
реагирование на который было признано необходимым, сразу по возв-
ращении процесса в режим задачи выполняется заранее условленное
действие, однако прежде чем перевести процесс в режим задачи, яд-
ро еще должно предпринять следующие шаги:
1. Ядро обращается к сохраненному регистровому контексту задачи
и выбирает значения счетчика команд и указателя вершины сте-
ка, которые будут возвращены пользовательскому процессу.
2. Сбрасывает в пространстве процесса прежнее значение поля
функции обработки сигнала и присваивает ему значение по умол-
чанию.
3. Создает новую запись в стеке задачи, в которую, при необходи-
мости выделяя дополнительную память, переписывает значения
счетчика команд и указателя вершины стека, выбранные ранее из
сохраненного регистрового контекста задачи. Стек задачи будет
выглядеть так, как будто процесс произвел обращение к пользо-
вательской функции (обработки сигнала) в той точке, где он
вызывал системную функцию или где ядро прервало его выполне-
ние (перед опознанием сигнала).
4. Вносит изменения в сохраненный регистровый контекст задачи:
устанавливает значение счетчика команд равным адресу функции
обработки сигнала, а значение указателя вершины стека равным
глубине стека задачи.
Таким образом, по возвращении из режима ядра в режим задачи
процесс приступит к выполнению функции обработки сигнала; после
ее завершения управление будет передано на то место в программе
пользователя, где было произведено обращение к системной функции
или произошло прерывание, тем самым как бы имитируется выход из
системной функции или прерывания.
В качестве примера можно привести программу (Рисунок 7.9),
которая принимает сигналы о прерывании (SIGINT) и сама посылает
их (в результате выполнения функции kill). На Рисунке 7.10 предс-
тавлены фрагменты программного кода, полученные в результате дис-
ассемблирования загрузочного модуля в операционной среде VAX
11/780. При выполнении процесса обращение к библиотечной процеду-
ре kill имеет адрес (шестнадцатиричный) ee; эта процедура в свою
очередь, прежде чем вызвать системную функцию kill, исполняет ко-
манду chmk (перевести процесс в режим ядра) по адресу 10a. Адрес
возврата из системной функции - 10c. Во время исполнения систем-
ной функции ядро посылает процессу сигнал о прерывании. Ядро об-
ращает внимание на этот сигнал тогда, когда процесс собирается
вернуться в режим задачи, выбирая из сохраненного регистрового
контекста адрес возврата 10c и помещая его в стек задачи. При
этом адрес функции обработки сигнала, 104, ядро помещает в сохра-
ненный регистровый контекст задачи. На Рисунке 7.11 показаны
различные состояния стека задачи и сохраненного регистрового кон-
текста.
В рассмотренном алгоритме обработки сигналов имеются некото-
рые несоответствия. Первое из них и наиболее важное связано с
очисткой перед возвращением процесса в режим задачи того поля в
пространстве процесса, которое содержит адрес пользовательской
функции обработки сигнала. Если процессу снова понадобится обра-
ботать сигнал, ему опять придется прибегнуть к помощи системной
функции signal. При этом могут возникнуть нежелательные последс-
--------------------------------------------┐
│ #include
│ main() │
│ { │
│ extern catcher(); │
│ signal(SIGINT,catcher); │
│ kill(0,SIGINT); │
│ } │
│ │
│ catcher() │
│ { │
│ } │
L--------------------------------------------
Рисунок 7.9. Исходный текст программы приема сигналов
---------------------------------------------------------┐
│ **** VAX DISASSEMBLER **** │
│ │
│ _main() │
│ e4: │
│ e6: pushab Ox18(pc) │
│ ec: pushl $Ox2 │
│ # в следующей строке вызывается функция signal │
│ ee: calls $Ox2,Ox23(pc) │
│ f5: pushl $Ox2 │
│ f7: clrl -(sp) │
│ # в следующей строке вызывается библиотечная процеду-│
│ ра kill │
│ f9: calls $Ox2,Ox8(pc) │
│ 100: ret │
│ 101: halt │
│ 102: halt │
│ 103: halt │
│ _catcher() │
│ 104: │
│ 106: ret │
│ 107: halt │
│ _kill() │
│ 108: │
│ # в следующей строке вызывается внутреннее прерывание│
│ операционной системы │
│ 10a: chmk $Ox25 │
│ 10c: bgequ Ox6
│ 10e: jmp Ox14(pc) │
│ 114: clrl r0 │
│ 116: ret │
L---------------------------------------------------------
Рисунок 7.10. Результат дисассемблирования программы приема
сигналов
До После
│ │ │ │
│ │ --->+--------------------+
│ │ Вершина │ │ Новая запись с вы- │
│ │ --- стека --- │ зовом функции │
│ │ │ задачи │ │
│ │ │ >│Адрес возврата (10c)│
+--------------------+<--- +--------------------+
│ Стек задачи │ │ Стек задачи │
│ до │ │ до │
│ получения сигнала │ │ получения сигнала │
L--------------------- L---------------------
Стек задачи Стек задачи
---------------------┐ ---------------------┐
│ Адрес возврата │ │ Адрес возврата │
│ в процессе (10c) │ │ в процессе (104) │
+--------------------+ +--------------------+
│ Сохраненный регист-│ │ Сохраненный регист-│
│ ровый контекст за- │ │ ровый контекст за- │
│ дачи │ │ дачи │
L--------------------- L---------------------
Системный контекстный Системный контекстный
уровень 1 уровень 1
Область сохранения Область сохранения
регистров регистров
Рисунок 7.11. Стек задачи и область сохранения структур ядра
до и после получения сигнала
твия: например, могут создасться условия для конкуренции, если
второй раз сигнал поступит до того, как процесс получит возмож-
ность запустить системную функцию. Поскольку процесс выполняется
в режиме задачи, ядру следовало бы произвести переключение кон-
текста, чтобы увеличить тем самым шансы процесса на получение
сигнала до момента сброса значения поля функции обработки сигна-
ла.
Эту ситуацию можно разобрать на примере программы, представ-
ленной на Рисунке 7.12. Процесс обращается к системной функции
signal для того, чтобы дать указание принимать сигналы о прерыва-
ниях и исполнять по их получении функцию sigcatcher. Затем он по-
рождает новый процесс, запускает системную функцию nice, позволя-
ющую сделать приоритет запуска процесса-родителя ниже приоритета
его потомка (см. главу 8), и входит в бесконечный цикл. Порожден-
ный процесс задерживает свое выполнение на 5 секунд, чтобы дать
родительскому процессу время исполнить системную функцию nice и
снизить свой приоритет. После этого порожденный процесс входит в
цикл, в каждой итерации которого он посылает родительскому про-
цессу сигнал о прерывании (посредством обращения к функции kill).
Если в результате ошибки, например, из-за того, что родительский
процесс больше не существует, kill завершается, то завершается и
порожденный процесс. Вся идея состоит в том, что родительскому
процессу следует запускать функцию обработки сигнала при каждом
получении сигнала о прерывании. Функция обработки сигнала выводит
сообщение и снова обращается к функции signal при очередном появ-
лении сигнала о прерывании, родительский же процесс продолжает
-------------------------------------------------------------┐
│ #include
│ sigcatcher() │
│ { │
│ printf("PID %d принял сигнал\n",getpid()); /* печать │
│ PID */ │