Главная страница Межпроцессное взаимодействие (состязание) они используются соответственно для обращения к портам ввода/вывода, сегментам памяти и регистрам процессора. Структуры также могут зависеть от целевой платформы. Например, существует структура 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. Этот уровень запрещает процессам выполнять некоторые инструкции, например инструкции ввода/вывода, управления распределением памяти и смены привилегий. Для тех, кто изучал современные процессоры, концепция нескольких уровней защиты по привилегиям наверняка понятна, но те, кто знакомился
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |