Главная страница Межпроцессное взаимодействие (состязание) Три функции из файл i8259.c вызываются при запуске системы для инициализации контроллеров прерываний на чипах Intel 8259. Функция intrjnit инициализирует контроллеры, записывая данные в несколько портов. В нескольких строках кода проверяется значение переменной, зависящей от параметров зафузки ядра, с целью учесть различные модели компьютеров. Параметр mine проверяется для того, чтобы записать в порт значение, соответствующее либо MINIX, либо ПЗУ BIOS. Когда MINIX завершает свою работу, функция intrjnit может восстановить векторы BIOS, тем самым корректно передавая управление монитору зафузки. Параметр mine определяет, какой режим будет использоваться. Полное понимание того, что происходит в функциях этого файла, требует изучения документации чипа 8259, поэтому мы не будем вдаваться в детали. Отметим лишь, что вызов out byte, помеченный комментарием IRQ 0-7 mask*, приводит к тому, что основной контроллер перестает откликаться на сигналы подчиненного, а аналогичная инструкция шестью строками далее блокирует реакцию подчиненного контроллера на все его входы. Кроме того, последняя инструкция этой функции загружает в каждую ячейку таблицы irq table адрес функции spuriousjrq. Это гарантирует, что прерывания, которые произойдут до того, как будут назначены реальные обработчики, не причинят вреда. Последняя функция в файле i8259.c - putjrq handler. При инициализации каждая из задач ввода/вывода, отвечающая на определенные прерывания, переписывает с ее помощью указатель на функцию-обработчик. В файле protect.c находятся несколько функций, необходимых для работы защищенного режима процессоров Intel. В памяти выделаются глобальная таблица дескрипторов (Global Descriptor Table, GDT), локальные таблицы дескрипторов (Local Descriptor Table, LDT) и таблица дескрипторов прерываний (Interrupt Descriptor Table, IDT), необходимые для предоставление защищенного доступа к системным ресурсам. Адреса GDT и LDT хранятся в специальных регистрах процессора, а записи в GDT содержат ссылки на отдельные локальные таблицы дескрипторов. GDT должна быть доступна для всех процессов и содержит дескрипторы сегментов для областей памяти, используемые операционной системой. Дескрипторы представляют собой структуры объемом 8 байт, состоящие из нескольких компонентов. Важнейшие из полей хранят адрес и размер области памяти. Таблица дескрипторов прерываний также состоит из 8-байтовых дескрипторов, в которых важнейшее поле хранит адрес кода, который будет выполнен при возникновении прерывания. Функция protjnit вызывается из start.c, чтобы установить GDT. BIOS IBM PC требует того, чтобы эта таблица была упорядочена определенным образом, для чего в файле protect.h описаны необходимые значения индексов. Область памяти для LDT выделяется в таблице процессов. Каждая такая таблица содержит два дескриптора: один для сегмента кода и один для сегмента данных. Заметьте: сегменты, которые мы обсуждаем, это сегменты, как их воспринимает аппаратное обеспечение системы. Это не то же самое, что сегменты, с которыми работает операционная система. Так, заданный аппаратно сегмент данных будет далее разбит на сегменты данных и стека. Для каждой из LDT создаются дескрипторы, помещаемые в GDT. За это ответственны функции init dataseg и init codeseg. За- писи в самой LDT инициализируются в тот момент, когда меняется карта памяти процесса (то есть когда делается системный вызов exec). Еще одна системная структура данных, которую необходимо инициализировать, это сегмент состояния задачи (Task State Segment, TSS). Его структура определяется в начале файла и включает в себя области для хранения регистров процессора, а также другой информации, которую необходимо запоминать при переключении задач. В MINIX используются только те поля, которые определяют, где будет создан новый стек в момент возникновения прерывания. Вызов init dataseg гарантирует, что этот стек может быть найден через GDT. Чтобы понять, как MINIX работает на нижнем уровне, важнее всего разобраться, как прерывания, исключения и инструкции int <nnn> приводят к исполнению различных процедур, написанных для их обслуживания. Для этого необходима таблица дескрипторов шлюзов прерываний. Массив gate table заполняется компилятором адресами процедур, которые обрабатывают исключения и аппаратные прерывания. Затем значительная часть этого массива в цикле инициируется при помощи вызовов функции int gate. Незаполненные векторы, SYS.VECTOR, SYS386 VECT0R и LEVELO VECTOR, требуют другого уровня привилегий и заполняются после цикла. Существует несколько причин, в силу которых данные определенным образом структурированы в виде дескрипторов. Основная мотивация - особенности аппаратного обеспечения и необходимость поддерживать совместимость между современными и старыми 16-битными процессорами. К счастью, детали мы можем оставить разработчикам процессоров Intel. Язык программирования С позволяет по большей части избегать таких мелочей. Тем не менее при разработке операционной системы так или иначе придется с ними столкнуться. Внутренняя структура одного дескриптора сегмента приведена на рис. 2.21. Обратите внимание на то, что базовый адрес, к которому программы на С обращаются как к простому 32-разрядному целому числу, разбит на три части, две из которых разделены 1-, 2- и 4-битными блоками. Размер области является 20-битным числом, которое хранится в виде пары блоков из 16 и 4 бит. Этот размер может интерпретироваться либо как количество байтов, либо как количество страниц объемом 4096 байт, в зависимости от значения бита G. У других дескрипторов другая структура, не менее сложная. Более подробно мы обсудим эти структуры в главе 4.
Относительный адрес Рис. 2.21. Формат дескриптора сегмента у Intel Большинство остальных функций из protect.c предназначены для преобразования данных между переменными в профаммах на С и тем сложным представлением, которое принято в дескрипторах. Функции init codeseg и init dataseg работают сходным образом. Их назначение в том, чтобы преобразовать переданные им данные в дескрипторы сегментов. Обе они, чтобы завершить свою работу, в свою очередь вызывают другую функцию, sdesc. Именно она имеет дело с запутанной структурой, которая показана на рис. 2.21. Функции init codeseg и init dataseg вызываются не только при инициализации системы. Помимо этого, они вызываются системой каждый раз, когда запускается новый процесс, чтобы выделить новому процессу необходимые сегменты памяти. Вызов функции seg2phys выполняется только из файла start.c, ее действие обратно действию sdesc - извлечение базового адреса сегмента из дескриптора. Функция int gate, аналогичная init codeseg и init dataseg, заполняет ячейки таблицы дескрипторов прерываний. Последняя функция в protect.c, enablejop, необходима для выполнения одного фязного трюка. Мы указывали на то, что одно из назначений операционной системы в том, чтобы ограничивать доступ к системным ресурсам, и один из способов, которым это реализуется в MINIX, являются уровни привилегий. Тем не менее MINIX может использоваться и в малых системах, рассчитанных всего на одного-нескольких пользователей, которым оказывается доверие. На таких системах пользователю может потребоваться написать программу, не брезгующую инструкциями ввода/ вывода, например, чтобы накапливать данные эксперимента. У файловой системы есть один меленький секрет, который встроен в ее код. Когда открывается один из файлов /dev/mem или /dev/kmem, задача памяти вызывает функцию enablejop, которая изменяет уровень привилегий для операций ввода/вывода, позволяя текущему процессу выполнять инструкции, считывающие данные из порта ввода/вывода и записывающие их. Описание того, что делает эта функция, сложнее, чем ее код, который сводится к установке двух битов в одном из слов кадра стека вызвавшего функцию процесса. Это слово будет загружено в регистр состояния процессора при следующем запуске процесса. 2.6.11. Утилиты и библиотека ядра у ядра есть библиотека вспомогательных функций, написанных на языке ассемблера. Эти функции подключаются при компиляции klib.s. Также имеется несколько вспомогательных профамм на языке С, которые находятся в файле misc.c. Сначала мы рассмотрим ассемблерные файлы. Файл klib.s представляет собой короткий ассемблерный файл, сходный с mpx.s. В нем в зависимости от значения WORD SIZE выбирается одна из версий кода. Код, который мы будем обсуждать, расположен в файле klib386.s. В этом файле содержится около двух дюжин вспомогательных функций, которые написаны на ассемблере либо по соображениям эффективности, либо потому, что они вообще не могут быть реализованы на С. Функция monitor позволяет вернуться из системы в монитор зафузки. С его точки зрения, вся операционная система - не более, чем подпрофамма, и, когда MINIX запускается, адрес монитора остается в стеке. Функции monitor необходимо только восстановить значения селекторов сегментов и указателя стека, после чего выполнить возврат из подпрограммы.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |