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

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

функция файла mpx386.s - idle task. Это пустой цикл, отрабатывающий тогда, когда нет других готовых к выполнению процессов.

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

.sect.гот

гарантирует, что эта область памяти находится в самом начале сегмента данных ядра. Сюда компилятор помещает сигнатуру (магическое число), чтобы программа boot могла убедиться, что загружает действительно ядро. После загрузки программа boot перезаписывает на это место данные из массива sizes, как это уже было показано нами ранее, при обсуждении структур данных ядра. Поэтому выделяемая память должна вмещать массив sizes, который может содержать до шестнадцати элементов (в случае, если включены дополнительные серверы). Еще одна область данных задается директивой

.sect.bss

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

2.6.8. Взаимодействие между процессами в MINIX

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

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

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



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

w status = in byte(w wn->base + REG STATUS): /* подтверждение прерывания */

interrupt(WINCHESTER):

return 1;

Если бы не требовалось считывать данные с порта ввода/вывода дискового контроллера, чтобы получить состояние, то вызов interrupt можно было бы поместить прямо в mpx386.s, а не в at wini.c. Первое, что делает interrupt, - проверяет, обслуживались ли другие прерывания в тот момент, когда произошло текущее, по значению переменной k reenter. Если такие прерывания имеются, текущее прерывание ставится в очередь и выполняется возврат из функции. Поставленное в очередь прерывание будет обслужено позже, когда будет сделан вызов unhold. Далее делается проверка, ожидает ли задача прерывания. Если задача не готова, выставляется флаг p int blocked и сообщение не отправляется. Как мы увидим позже, установка этого флага позволяет восстановить необслуженные прерывания. Отправить сообщение от аппаратуры задаче несложно, так как задачи и ядро скомпонованы в один файл и могут обращаться к структурам данных друг друга. Отправляемое сообщение копируется в приемный буфер ожидающей задачи, у которой сбрасывается флаг RECEIVING, с целью ее разблокировать. После того как сообщение отправлено, получившая его задача ставится на первое место в очереди планировщика. Более подробно мы обсудим работу планировщика в следующем разделе, а сейчас вы можете рассматривать дальнейший код в interrupt как предварительный пример, поскольку он соответствует процедуре ready, ставящей процесс в очередь. Этот код проще, так как здесь сообщения могут отправляться только задачам, и нет необходимости определять, какую из трех очередей модифицировать.

Далее в файле ргос.с следует функция sys calL Ее назначение аналогично назначению interrupt, поскольку эта функция преобразует программное прерывание (инструкция int SYS386 VECT0R служит для инициации системного вызова) в сообщение. Так как в данном случае диапазон возможных адресатов сообщений более широк, а также может потребоваться либо отправка, либо прием сообщения, либо и то и другое, то у sys call работы больше. Как это часто бывает в подобных ситуациях, это означает, что код sys call проще и понятней, все-таки большую часть своих задач эта функция выполняет, вызывая другие процедуры. Первый такой вызов - isoksrc dest, макрос, определенный в proc.h. Этот макрос использует другой макрос, isokprocn, который также задан в файле proc.li и проверяет, корректно ли указан адресат или отправитель сообщения. Далее следует макрос isuserp, нужный, чтобы убедиться, что пользовательский процесс собирается отправить сообщение и ждать ответа - единственное, что разрешено пользовательским процессам. Нарушения этих условий маловероятны, но проверку сделать просто, поэтому проверяющий код сводится к сравнению небольших



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

Наконец, если вызов требует отправки сообщения, вызывается функция mini send, а если требуется принять сообщение, вызывается mini rec. Эти две функции - сердце механизма обработки сообщений и заслуживают тщательного изучения.

У mini send три входных параметра: отправитель, адресат и указатель на буфер, в котором расположено сообщение. Функция проводит ряд проверок входных данных. Прежде всего, удостоверяется, что пользовательский процесс пытается отправить сообщение либо файловой системе, либо менеджеру памяти. Значение аргумента caller ptr тестируется макросом isuserp, который убеждается, что отправитель является пользовательским процессом. Аргумент dest тестируется при помощи аналогичного макроса, issysentn, проверяющего, является ли процесс файловой системой или менеджером памяти. Если обнаружено несоответствие, mini send завершается с ошибкой.

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

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

Следующая проверка является ключевой для mini send. А именно, проверяется, находится ли адресат в состоянии блокировки после вызова receive. Для этого анализируется значение бита RECEIVING в поле p flags дескриптора процесса. Если получатель ждет, выясняется, от кого он ждет сообщение. Есди объектом интереса является отправитель или ANY, срабатывает макрос CopyMess, что приводит к копированию данных в буфер адресата и разблокированию последнего. Этот макрос вызывает ассемблерную процедуру cp mess из файла klib386.s.

Если же адресат не блокирован или блокирован, но ждет сообщение от другого процесса, выполняется код, переводящий в состояние блокировки уже отправителя. Все процессы, ожидающие отправки сообщения одному и тому же адресату, связываются вместе в список, на голову которого указывает поле p caUerq адресата. Пример на рис. 2.19, а показывает, что произойдет, если процесс 3 не сможет отправить сообщение процессу 0. Если при этом процесс 4 также не может отправить сообщение процессу О, возникает ситуация, продемонстрированная на рис. 2.20, б.

Перейдем к рассмотрению функции mini rec. Она вызывается, когда sys caU вызывается с параметром RECEIVE или BOTH. Сначала цикл ищет среди процессов, ожидающих отправки, нужного отправителя. Если один из них удовлетворяет условиям, сообщение копируется из буфера отправителя в буфер приемника, затем отправитель выводится из блокировки и исключается из очереди процессов, ждущих отправки сообщения.



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.
Копирование материалов разрешено исключительно при условии цититирования.