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

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

4.8. Реализация управления памятью в MINIX

Вооружившись общим пониманием того, как работает менеджер памяти, давайте обратимся к самому коду. Менеджер памяти полностью написан на С, его код прямолинеен и снабжен значительным количеством комментариев, посему по большей части мы не будем слишком глубоко вдаваться в детали. Сначала мы изучим заголовочные файлы, затем главную программу, а потом файлы с кодом описанных ранее системных вызовов.

4.8.1. Заголовочные файлы и структуры данных

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

Как и у всех остальных основополагающих компонентов системы MINIX, у диспетчера памяти есть свой главный заголовочный файл, mmh.h. Он включается в каждый файл с кодом и, в свою очередь, включает в себя все общесистемные заголовочные файлы из каталога/usr/include и его подкаталогов, нужные каждому объектному модулю. С ним присоединяется большая часть файлов, включаемых в kernel/kernrLh, а также собственные версии const.h, type.h, proto.h и glo.h.

Файл const.h задает ряд необходимых менеджеру памяти констант. Строка

#define printf printk

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

Файл type.h в текущей версии не используется и содержит только скелет, для того лишь, чтобы структура менеджера памяти была такой же, как и прочих частей системы. Файл proto.h предназначен для того, чтобы в одном месте собрать прототипы всех необходимых менеджеру памяти функций.

Глобальные переменные менеджера памяти декларируются в файле glo.h. Здесь применен тот же самый трюк с EXTERN, что и в ядре. А именно, макрос EXTERN разворачивается в ключевое слово extern во всех файлах, за исключением table.c, где он порождает пустую строку. В результате в файле table.c резервируется память под хранение глобальных переменных.



Первая из глобальных переменных, тр, является указателем на структуру mproc. Эта структура описывает ту часть таблицы процессов, которая принадлежит менеджеру памяти, а переменная тр ссылается на процесс, системный вызов которого обрабатывается в текущий момент. Вторая переменная, dont reply, по прибытии каждого запроса инициализируется значением FALSE, но если в процессе обработки запроса обнаруживается, что отправлять ответное сообщение не нужно, ей может быть присвоено значение TRUE. В качестве примера вызова, при выполнении которого не требуется отправлять ответное сообщение, можно привести успешный вызов exec. Третья переменная, procsJn use, служит для подсчета имеющихся в текущий момент процессов, эта цифра помогает выяснить, выполним ли вызов fork.

Буферы сообщений mmjn и mm out предназначены соответственно для запросов и ответных сообщений. Переменная who содержит индекс текущего процесса. Она связана с переменой тр следующим соотношением:

mp-&iT)proc[who]:

Когда получен системный вызов, его номер извлекается из сообщения и помещается в переменную mm call.

Три переменные, err code, result2 и resp ptr, служат для хранения значений, возвращаемых в ответном сообщении процессу, сделавшему вызов. Важнейшей из этой тройки для нас является переменная err code, которая, если нет ошибки, устанавливается в значение ОК. Две последние переменные нужны тогда, когда обнаруживается ошибка. Если процесс завершается аномально, MINIX сбрасывает в файл образ процесса. Имя этого файла задается переменной core name,

а битовая карта core sset описывает, какие сигналы должны приводить к дампу

памяти.

Та часть таблицы процессов, которой заведует менеджер памяти, описана в следующем файле, mproc.h. По большей части, назначение полей этой таблицы объясняется комментариями. Несколько полей связаны с обработкой сигналов. Поля mpjgnore, mp catch, mp sigmask, mp sigmask2 и mp sigpending представляют собой битовые карты, в которых каждый бит означает один из сигналов, разрешенный к отправке процессу. Эти поля имеют тип sigset t, являющийся 32-разрядным целым числом, следовательно, MINIX может поддерживать до 32 сигналов, но сейчас определено только 16 сигналов, причем сигналу № 1 соответствует наименее значимый (самый правый) бит карты. Впрочем, согласно POSIX, требуются специальные функции для добавления и удаления сигналов в эти наборы, поэтому при манипуляциях с битовыми картами программисту не нужно вдаваться в такие детали. А вот массив mp sigact важен для обработки сигналов. В нем имеется по одной структуре типа sigaction (файл include/signaLh) на каждый возможный тип сигнала. Эта структура составлена из трех полей:

♦ sa handler - определяет, будет ли для сигнала выполнено действие по умолчанию, специальное действие по обработке, или же сигнал будет игнорирован;

♦ sa mask - является битовой картой, в которой отмечается, какие сигналы были заблокированы во время выполнения пользовательского обработчика;

♦ sa flags - содержит набор флагов сигнала.



Благодаря массиву mp sigact обеспечивается большая гибкость при обработке сигналов.

Поле mp flags, как показано в конце файла, необходимо для хранения разнообразных битовых сочетаний. Хранимое в нем - это что-либо беззнаковое целое, 16-разрядное для самых старых процессоров и 32-разрядное для процессоров 386 и старше. Поле используется далеко не в меру своей емкости, на машинах 8088 задействованы только девять из битов.

Последнее поле таблицы процессов называется mp procargs. Когда запускается новый процесс, строится стек, подобный показанному на рис. 4.35, и указатель на массив argv нового процесса сохраняется в этой переменной. Например, на рис. 4.35 в поле будет сохранено значение 8164, благодаря чему, пока выполняется команда Is, команда ps может вывести содержимое командной строки:

Is -1 f.c g.c

Следующий файл, param.h, содержит макросы, помогающие формировать сообщения для многих системных вызовов. Кроме того, здесь же описаны четыре макроса для формирования ответных сообщений. Когда в любом файле, куда включен param.h, появляется такое выражение:

к = pid:

перед компиляцией препроцессор преобразует его следующим образом:

к = mm in.ml il:

Давайте еще раз взглянем на файл table.c. При его компиляции получается объектный файл, в котором выделяется место для хранения глобальных переменных и структур, объявленных с директивой EXTERN. Такие структуры мы видели в glo.h и mproc.h. Благодаря тому, что в этом файле присутствует выражение

#define JABLE

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

Еще один важный элемент файла table.c - массив call vec. С его помощью номер системного вызова преобразуется в адрес выполняющей его функции, номер вызова играет роль индекса в массиве. Если в сообщении указан несуществующий номер системного вызова, управление передается функции no sys, которая просто возвращает код ошибки. Несмотря на то что при объявлении массива call vec применяется макрос PROTOTYPE, в действительности это не описание прототипа, а описание инициализированного массива. Но так как он же массив указателей на функции, проще всего получить код, компилируемый как классическим (Керниган и Ричи), так и стандартным компилятором С при помощи макроса PROTOTYPE.

4.8.2. Основная программа

Менеджер памяти компилируется и компонуется независимо от ядра и файловой системы. Следовательно, у него имеется своя собственная главная процедура.



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