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

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

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

Структуры также могут зависеть от целевой платформы. Например, существует структура stackframe t, определяющая то, как машинные регистры сохраняются в стеке, для процессоров Intel. Это чрезвычайно важная структура - она играет свою роль при сохранении и восстановлении состояния процессора, когда процесс приостанавливается или возвращается в состояние выполнения. То, насколько эффективно ассемблерный код будет читать или записывать поля структуры, во многом обусловливает время, необходимое на переключение между контекстами. Segdesc s - еще одна структура, относящаяся к процессорам Intel. Она связана с механизмом защиты, обеспечивающим разграничение доступа процессов к памяти.

Для процессоров Intel, которые могут быть как с 16-, так и 32-разрядными регистрами, тип reg t определен как unsigned. Для процессоров Motorola этот тип описан как u32 t. Таким процессорам также нужна структура stackframe s, но расположение полей в ней иное, с целью ускорения выполнения ассемблерного кода. Архитектура процессоров Motorola такова, что для них не требуются ни типы seg t и port t, ни структура segdesc s. С другой стороны, для архитектуры Motorola характерны несколько определений, у которых нет аналогов в коде для Intel.

Следующий файл, который мы рассмотрим, proto.h - один из самых длинных заголовочных файлов из всех представляющих для нас сейчас интерес. В нем находятся прототипы всех функций, которые должны быть видны за пределами тех файлов, где они объявлены. Все прототипы описаны при помощи макроса .PROTOTYPE, о котором говорилось в предыдущем разделе, благодаря нему ядро MINIX может быть скомпилировано как классическим компилятором С (Керниган и Ричи), так и более современным, соответствующим стандарту ANSI С, такой компилятор входит в комплектацию MINIX версии 2. Отдельные прототипы зависят от системы, к ним относятся обработчики прерываний и исключений, а также функции на языке ассемблера.

Последний из заголовочных файлов, включаемых в kernel.h, - файл glo.h. В нем мы найдем глобальные переменные ядра. Макрос EXTERN, используемый для описания переменных, уже обсуждался при рассмотрении файла include/ minix/const.h. Обычно он разворачивается в единственное ключевое слово extern. Но когда этот заголовочный файл включается в table.c, где установлен макрос TABLE, макрос EXTERN сбрасывается, поэтому он раскрывается в пустую строку. Смысл файла glo.h в том, чтобы позволить использовать в других файлах глобальные переменные из table.c. А именно: held head и held tail, которые являются указателями на начало и конец очереди ожидающих прерываний. Переменная proc ptr ссылается на запись в таблице процессов, соответствующую текущему процессу. Зная эту запись, мы знаем, куда сохранять значения регистров и состояние процессора при возникновении прерывания. Переменная Sig procs хранит количество процессов, которым поступили сигналы, еще не переданные менеджеру памяти на обработку.



Ряд записей в glo.h объявлены с ключевым словом extern, а не с макросом EXTERN. К ним относятся: sizes, массив, заполняемый монитором загрузки, таблица задач - tasktab, и стек - t stack. Это связано с тем, что невозможно совмещение макроса EXTERN с инициализацией переменных в стиле С, поскольку каждая переменная должна инициализироваться всего единожды.

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

Есть еще два широко используемых заголовочных файла, относящихся к ядру, хотя они нужны и не так часто, как те, что включены в kerneLh. Первый - proc.h, описывает структуру proc, являющуюся элементом таблицы процессов. В этом же файле далее определяется сама таблица процессов, как массив таких структур, proc[NR TASKS + NR PROCS]. Макроопределение NR TASKS находится в файле include/minix/const.h, а NR TASKS - в include/minix/config.h. Вместе эти два макроса задают размер таблицы процессов. Значение макроса NR PROCS можно менять, если надо построить систему, способную работать с большим количеством пользователей. Так как обращения к таблице процессов происходят часто, а вычисление адреса в массиве требует умножения чисел - довольно медленной операции, то для ускорения доступа к записям организуется массив указателей, pproc addr.

Каждый элемент таблицы процессов хранит регистры процесса, указатель стека, состояние процесса, карту памяти, предельный размер стека, идентификатор процесса, информацию о времени срабатывания таймера, сообщениях и прочие сведения о процессе. Первая часть каждого элемента таблицы - это структура stackframe s. Когда процесс переходит в состояние выполнения, его указатель стека восстанавливается по адресу из записи в таблице процессов, и все регистры процессора восстанавливаются из этой структуры. В тех случаях, когда процесс не может выполнить вызов send из-за того, что получатель сообщения не ожидает его, процесс помещается в очередь, на которую указывает поле p callerq у процесса-получателя. Таким образом, когда получатель делает вызов receive, можно легко найти все процессы, которые послали ему сообщения. Для того чтобы связать элементы очереди в единый список, используется поле p sendlink.

Когда процесс делает вызов receive, а очередь сообщений пуста, процесс блокируется и идентификатор процесса-инициатора сообщения сохраняется в поле p getfrom. При этом адрес буфера, в который должно быть помещено сообщение, заносится в поле p messbuf. Последние три поля у каждой записи в таблице процессов: p nextready, p pending и p pendcount. Первое из них позволяет связать вместе процессы в очереди планировщика, второе представляет собой битовую карту для отслеживания сигналов, еще не переданных менеджеру памяти (если менеджер памяти не находится в ожидании сообщения). Третье поле содержит счетчик этих сигналов.

Флаговые биты в p flags описывают состояние каждой записи в таблице процессов. Если хотя бы один из них установлен, процесс не может перейти в состояние выполнения. Если элемент таблицы не используется, значит, установ-



лен флаг P SLOT FREE. После выполнения вызова fork устанавливается флаг NO MAP, чтобы дочерний процесс не был запущен до того, как сформирована его карта памяти. Флаги SENDING и RECEIVING индицируют блокировку процесса, соответственно, отправляющего сообщение или ожидающего его поступления. Флаги PENDING и SIG PENDIN6 фиксируют факт получения сигналов, и, наконец, флаг P STOP необходим для трассировки процесса при отладке.

Потребность в макросе proc addr проистекает из того, что в С нельзя использовать отрицательные индексы в массивах, как для указателей. Теоретически индексы в массиве ргос должны принимать значения из диапазона [-NR TASKS ... +NR PROCS]. К сожалению, в С индексы должны начинаться с О, поэтому ргос[0] соответствует наименьшему отрицательному индексу и т. д. Чтобы было проще отслеживать, какая запись соответствует какому процессу, можно использовать макрос и просто писать: гр = proc addrCn),

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

Указатель bill ptr ссылается на процесс, ответственный за процессор. Когда пользовательский процесс вызывает файловую систему и она выполняется, то proc ptr указывает на процесс файловой системы. Вместе с тем bilLptr будет указывать на пользовательский процесс, и время, которое работала файловая система, учитывается во времени работы пользовательского процесса.

Два массива, rdy head и rdy tail, необходимы для поддержания очередей планировщика. Так, например, на первый процесс в очереди задач указывает rdy head[TASK Q].

Теперь рассмотрим другой заголовочный файл, который также включается в большое число различных файлов с исходными кодами, protect.h. Почти все, что есть в этом файле, относится к архитектуре процессоров Intel с поддержкой защищенного режима (это процессоры старше 80286). Детальное рассмотрение Intel-процессоров не входит в задачи книги. Достаточно только сказать, что у них есть ряд внутренних регистров, указывающих на таблииу дескрипторов в памяти. Информация из таблицы дескрипторов позволяет узнать, как используются ресурсы системы, предотвращать доступ процесса к памяти, принадлежащей другому процессу и др. В дополнение, архитектура таких процессоров предлагает четыре уровня привилегий, из которых в MINIX используются три. Центральная часть ядра, то есть код, имеющий дело с прерываниями и переключением контекста, работает с полномочиями уровня INTR PRIVILEGE. Процесс с таким уровнем доступа вправе обращаться к любым регистрам и любым ячейкам памяти. Задачи работают с уровнем привилегий TASK PRIVILEGE, который позволяет им обращаться к устройствам ввода/вывода, но не дает изменять значения некоторых специальных регистров, например указатели на таблицы дескрипторов. Серверы и пользовательские процессы выполняются на уровне USER PRIVILEGE. Этот уровень запрещает процессам выполнять некоторые инструкции, например инструкции ввода/вывода, управления распределением памяти и смены привилегий. Для тех, кто изучал современные процессоры, концепция нескольких уровней защиты по привилегиям наверняка понятна, но те, кто знакомился



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