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

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

стек переполняет свой сегмент. Хотя в этом режиме MINIX обрабатывает попытки обратиться к памяти за пределами сегмента (параметры которого определены дескриптором сегмента, см. рис. 4.26), в MINIX дескрипторы сегмента данных и сегмента стека всегда идентичны. В MINIX стек и данные находятся в едином адресном пространстве, и благодаря этому они могут расширяться за счет межсегментного зазора. Но это не более чем внутреннее представление MINIX. С точки зрения процессора, при обрашении к памяти между сегментами нет никакой ошибки, так как она является частью обшего сегмента данных и стека. Аппаратное обеспечение помогает отслеживать серьезные ошибки, например попытки обращения к памяти за пределами комбинированной области данные-за-зор-стек*, благодаря чему один процесс можно защитить от ошибок другого процесса, но уберечь процесс самого от себя таким образом нельзя.

MINIX это делает сознательно. Если у вас есть аргументы за то, чтобы отказаться от разделяемого аппаратного сегмента и зазора, мы не будем спорить. Альтернативой могут быть два раздельных аппаратных сегмента для стека и данных. Такой подход обеспечивает большую безопасность в отношении некоторых ошибок, но это превратило бы MINIX в пожирающее память чудовище. Исходные коды системы открыты для всех, кто желает поэкспериментировать с таким подходом.

На рис. 4.33, в видно, как будут определены сегменты в случае раздельных адресных пространств кода и данных. Здесь уже и сегмент стека, и сегмент данных имеют ненулевую длину. Показанный на рис. 4.33, бив массив mp seg большей частью применяется для преобразования виртуальных адресов в физические. Зная виртуальный адрес и адресное пространство, которому он принадлежит, несложно проверить, является этот адрес допустимым (то есть попадает ли он в сегмент), и рассчитать, какому физическому адресу он соответствует. Например, такое преобразование осуществляет вспомогательная подпрограмма umap, которую задачи ввода/вывода привлекают для обмена данными с пользовательскими процессами.

При выполнении процесса содержимое его областей данных и стека может меняться, но код не меняется никогда. Часто случается, что несколько процессов выполняют одну и ту же программу, например несколько пользователей могут пользоваться одной оболочкой. Поэтому разделяемый текст повышает эффективность памяти. Когда системный вызов exec собирается загрузить процесс, он открывает файл с образом этого процесса и считывает заголовок. Если у процесса раздельные адресные пространства кода и данных, среди всех ячеек таблицы mproc осуществляется поиск по полям mp dev, mpjno, mp ctime. Эти поля содержат информацию о номере г-узла и времени модификации образов, исполняемых другими процессами. Если обнаруживается процесс, который уже выполняет нужную программу, то выделять память под еще одну копию кода не нужно. Вместо этого в карту памяти нового процесса в поле mp seg[T] записывается указатель на ту область памяти, где уже хранится код, а память выделяется только под данные и стек. Это продемонстрировано на рис. 4.34. Если загруженного образа не было найдено или адресные пространства кода и данных объединены, память выделяется согласно рис. 4.33 и заполняется данными с диска.



Стек Данные Код

0x14

0x340

0х32с

0x10

0x320

Процесс 1


0x14

0x3d4

ОхЗсО

0x10

0x320

Стек

Данные

Процесс 2 в

Рис. 4.33. 8 - карта памяти для раздельных адресных пространств кода и данных, как на предыдущем рисунке; 6 - распределение памяти после запуска второго процесса, выполняющего тот же код; а - карта памяти второго процесса

Помимо информации о сегментах, mproc хранит идентификатор (PID) самого процесса и его родителя, идентификаторы пользователя и группы (реальное и эффективное значения), информацию о сигналах и код возврата, если процесс уже завершился, но его родитель еше не завершил вызов wait.

Таблица свободных участков памяти, hole, заданная в файле alloc.c, является другой важнейшей таблицей менеджера памяти. Записи в этой таблице расположены в порядке возрастания адресов памяти. Промежутки между сегментами стека и данных зарезервированы для определенного процесса и не считаются незанятыми участками памяти, поэтому они не входят в эту таблицу. Каждая запись таблицы дыр содержит три поля: базовый адрес свободного блока памяти, в кликах, длину блока, тоже в кликах, и указатель на следующую запись в списке. Список является однонаправленным, другими словами, зная одну запись, легко найти любую следующую, но если необходимо найти предшествующую запись, придется перебирать список с самого начала.

Причина, по которой положение сегментов и свободных блоков измеряется в кликах, а не в байтах, проста: это более эффективно. В 16-битном режиме для записи адресов памяти используются 16-битные целые числа, что позволяется при размере клика 256 байт поддерживать до 16 Мбайт памяти. В 32-разрядном режиме таким образом может адресоваться до 2° байт, что составляет 1024 Гбайт.

Основными действиями над списком дыр являются выделение блока заданного размера и освобождение ранее вьщеленного блока. При выделении блока



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

Когда процесс завершает свою работу и удаляется из памяти, память, вьще-ленная под данные и стек, возвращается в список свободных блоков. Если адресные пространства кода и данных процесса объединены, это означает, что возвращается вся занятая процессом память, поскольку отдельный блок под код здесь не предусмотрен. Если адресные пространства разделены и обнаруживается, что код процесса больше никем не используется, то занятая кодом память также высвобождается. При возврате блока в список дыр он, если это возможно, объединяется с соседними свободными блоками, соответственно, двух смежных дыр никогда не возникает. Таким образом, при работе системы постоянно меняются количество, положение и размер свободных участков памяти. Когда все пользовательские процессы завершатся, вся свободная память вновь будет готова к распределению. Она не обязательно будет образовывать один сплошной блок, так как физическая память может прерываться областями, недоступными даже операционной системе. Например, в IBM PC-совместимых системах доступна память ниже 640 Кбайт и выше 1 Мбайт, а промежуток между этими адресами занят ПЗУ и памятью, зарезервированной для операций ввода/вывода.

4.7.4. Системные вызовы fork, exit и wait

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

1. Проверить, заполнена ли таблица процессов.

2. Попытаться выделить память для данных и стека дочернего процесса.

3. Скопировать содержимое данных и стека родительского процесса в память потомка.

4. Найти свободную ячейку в таблице процессов и скопировать в нее запись родительского процесса.

5. Поставить на учет карту памяти дочернего процесса в таблице процессов.

6. Выбрать для дочернего процесса PID.

7. Передать информацию о потомке ядру и файловой системе.

8. Сообщить ядру сведения о карте памяти потомка.

9. Отправить дочернему и родительскому процессам ответное сообщение.



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