Главная страница  Межпроцессное взаимодействие (состязание) 

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 129 130 131 132 133 134 135 136 137 138 [ 139 ] 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

Выше мы упомянули несколько вспомогательных функций. Теперь рассмотрим их подробно. Важнейшей из них является sig proc, которая и отвечает за отправку сигнала. Сначала она делает несколько проверок. Попытка послать сигнал завершившемуся или потустороннему процессу является признаком серьезной ошибки и приводит к панике системы. Если процесс трассируется, то при получении сигнала он останавливается. Если же сигнал должен игнорироваться, работа sig proc завершается. Это действие принято по умолчанию для ряда сигналов, например для тех, которые задает стандарт POSIX, но не поддерживает MINIX. Если сигнал блокируется, нужно только установить соответствующий бит в битовой карте mp sigpending процесса. Ключевое значение имеет проверка, разделяющая процессы на тех, кому разрешено обрабатывать сигнал, от тех, кому нет (6-й оператор if в этой функции). С этого момента все прочие варианты исключаются, и процессы, не удостоившиеся права обработать сигнал, будут завершены.

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

Затем выделяется место в стеке процесса. Структура, которая помещается в стек, показана на рис. 4.38. В нее входит и структура sigcontext одного из полей таблицы процессов. Она сохраняется в стеке потому, что ее значение в таблице процессов меняется при подготовке к запуску обработчика. Часть, названная sigframe, содержит адрес возврата и данные, необходимые вызову sigreturn для восстановления состояния процесса. Адрес возврата и указатель кадра стека в действительности не используются нигде в MINIX. Они нужны для того, чтобы обмануть отладчик при трассировке обработчика событий.

Структура, помещаемая в стек, довольно объемна. Для нее выделяется место (декремент переменной new sp), а затем, чтобы проверить, достаточно ли байтов в стеке, вызывается adjust. Если места не хватает, происходит переход по метке determinate (здесь вы можете увидеть редко используемый и всеми гонимый оператор языка С goto), и процесс завершается.

Существует потенциальная проблема при вызове adjust. При обсуждении реализации системного вызова brk говорилось, что adjust начинает сообщать об ошибке тогда, когда стек приблизился к области данных на расстояние SAFETY BYTES. Дополнительный зазор вводится потому, что программно стек может проверяться лишь время от времени. В данном случае, вероятно, такой запас прочности излишен, так как достоверно известно, какая часть стека требуется для сигнала, а дополнительное место необходимо только обработчику, который предположительно является относительно простой функцией. Поэтому возможно, что неко-



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

Если в стеке достаточно места, проверяются значения еще двух флагов. Флаг SA NODEFER означает, что дальнейшие сигналы должны быть блокированы на время выполнения обработчика. Флаг SA RESETHAND - признак того, что при получении сигнала обработчик должен быть снят. (В результате получается честная имитация устаревшего вызова signal. Хотя такое поведение обычно рассматривалось как недостаток, при поддержке устаревших функций необходимо поддерживать и их недостатки.) Затем при помощи библиотечной процедуры sys sendsig отправляется уведомление ядру. В завершение сбрасывается бит-индикатор задержанного сигнала и вызывается функция unpause, которая прерывает любой системный вызов, остановивший процесс. В следующий раз, когда процесс получит управление, начнет работу обработчик.

Регистры процессора (64 байта)

Структура sigcontext

Структура sigframe

Маска

Флаги

Указатель на signcontext

Адрес возврата

Указатель фрейма

Указатель на signcontext

Код (плавающая запятая)

Номер сигнала

Адрес структуры sigreturn

Рис. 4.38. Чтобы подготовиться к запуску обработчика, в стек помещаются структуры sigcontext и sigframe. Регистры процессора копируются из кадра стека на момент

переключения контекстов

Теперь давайте взглянем на код завершения процесса, под меткой determinate. Оператор goto и метка - самый простой способ обработать возможный отказ при вызове adjust. Итак, сюда передается управление, если сигнал по тем или другим причинам не может или не должен быть обработан. В таком случае вызо-



BOM mm exit процесс всегда завершается (как если бы он хотел этого сам), а иногда, в зависимости от сигнала, может выводиться дамп.

В функции check sig менеджер памяти проверяет, может ли быть послан сигнал. Так, вызов

kill (O.sig):

означает, что указанный сигнал должен быть отправлен всем процессам в текущей группе (то есть всем процессам, запущенным с того же терминала). Сигналы, исходящие от ядра, и сигнал REBOOT также могут затрагивать несколько процессов. По этой причине check sig в цикле перебирает все процессы из таблицы процессов, выбирая из них потенциальных получателей. В цикле содержится много тестов, и только если все они пройдены, при помощи sig proc отправляется сигнал.

Еще одна функция, которая несколько раз вызывается в недавно рассмотренном нами коде, называется sig pending. Она перебирает все биты в битовой карте mp sigpending процесса, сверяясь с битовыми картами do sigmask, do sigretrun и do sigsLispend, и смотрит, не была ли снята блокировка с одного из задержанных сигналов. Обнаружив первый такой сигнал, она отправляет его. Так как все обработчики событий со временем вызывают do sigretrun, в результате доставляются все разблокированные задержанные сигналы.

Процедура unpause необходима для отправки сигналов процессам, приостановленным на одном из вызовов pause, wait, read, write или sigsuspend. Выяснить, обусловлена ли остановка вызовами pause, wait или sigsuspend, можно, сверившись с таблицей процессов менеджера памяти. Если та не дает ответа, запрос передается файловой системе, которая при помощи собственной функции do unpause проверяет, был ли процесс приостановлен на read или write. В любом случае, результат одинаков: незаконченный вызов завершается с ошибкой, а процесс вновь запускается и может обработать сигнал.

Последняя процедура в этом файле называется dump core, она записывает на диск дампы памяти. Дамп состоит из заголовка, содержащего информацию о размере занимаемых процессом сегментов, всей информации состояния процесса (это копия записи процесса из таблицы процессов ядра) и образов всех сегментов. Отладчик может прочитать эту информацию, чтобы помочь программисту определить, что пошло не так в работе программы. Код записи в файл прямолинеен. Здесь снова возникает потенциальная проблема, упомянутая в предьщущем разделе, но теперь в несколько иной форме. Чтобы гарантировать, что записываемый в дамп сегмент стека содержит самую последнюю информацию, вызывается adjust. Из-за проверки границы безопасности этот вызов может провалиться . Правда, дамп записывается в любом случае, независимо от успеха вызова, но информация о стеке может оказаться неправильной.

4.8.7. Прочие системные вызовы

Файл getset.c содержит только одну процедуру, do getset, выполняющую оставшиеся семь системных вызовов. Эти вызовы перечислены в табл. 4.6. Все они на-



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 129 130 131 132 133 134 135 136 137 138 [ 139 ] 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2018 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.