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

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

get block, и обработчик кэша блоков находит нужный блок, считывая его при необходимости.

О копировании данных в адресное пространство пользователя из найденного блока заботится функция sys copy на 270-й строке. Затем блок освобождается, чтобы позже он мог быть удален из кэша. (После того как get block находит нужный блок, тот исключается из списка LRU и пребывает вне его до тех пор, пока счетчик использования в заголовке буфера не обнулится. Вызов put block уменьшает значение этого счетчика и, если настало время, возвращает буфер в LRU-список). Код в 280-й строке файла определяет, заполнился ли блок при записи. Правда, сейчас это уже не принципиально, так как функция put block теперь игнорирует значение, передаваемое ей во втором аргументе, и всегда добавляет освободившиеся блоки в конец LRU-последовательности.

Функция read map преобразует логическое смещение в файле в физический номер блока, пользуясь для этого информацией i-узла. Те блоки, которые достаточно близки к началу файла, попадут в одну из первых семи зон (то есть в одну из тех зон, что хранятся в самом i-узле). При этом, чтобы узнать, какая из зон необходима, требуется только несложная арифметика. Для блоков, расположенных дальше, может понадобиться считать один или два блока косвенной адресации.

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

Функция read ahead преобразует логическое положение в физический адрес блока, вызывает get block, в результате чего блок оказывается в кэше, и немедленно возвращает его. В конце концов, сделать с ним она все равно ничего не может. Задача read ahead лишь в том, чтобы увеличить вероятность найти данные в кэше, если они скоро понадобятся.

Обратите внимание, что read ahead вызывается только из главного цикла в main. Ее вызов не является частью выполнения read. Важно понять, что эта функция вызывается после того, как пользователю отправлен ответ, чтобы он мог продолжать свою работу, пока файловая система дожидается завершения упреждающего чтения.

Сама функция read ahead написана так, чтобы запрашивать всего один блок. Реально трудится вызываемая ею подпрофамма rahead, последняя в файле read.c. В rahead заложена та жизненная концепция, что если немного больше - хорошо, то намного больше - еще лучше. Так как дискам и прочим накопителям часто требуется много времени, чтобы найти первый запрошенный блок, но зато они могут быстро считывать несколько смежных блоков, делается ставка на то, что получится считать много последовательных блоков ценой небольших дополнительных трудозатрат. Запрос на упреждающую выборку передается функции get block, подготавливающей кэш блоков к получению нескольких блоков за раз. Затем вызывается rw scattered, которой передается список блоков. Работу этой функции мы уже обсуждали. Вспомните, что rw scattered передает запрос драй-



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

На рис. 5.32 показаны взаимосвязи между основными подпрограммами, участвующими в чтении файла. В том числе указано, кто кого вызывает.

Точки входа

Главная процедура для чтения и записи

Специальные Каналы файлы ввода/вы

Определение дискового



;readjTiap<h


Передача Возврат данных от ФС блока

rdjndir J) (et bloc* пользователю в кэш

Доступ к косвенному блоку

(если нужно)Qrw block

(devjo


Поиск в кэше

(т Адрес в таблице тар

ядру

Рис. 5.32. Некоторые из процедур, участвующих в чтении файла

Запись

Код, обеспечивающий запись в файл, находится в write.c. Запись в файл в большинстве своем сходна с чтением, и do write просто вызывает read write с флагом WRITING. Основное отличие записи в том, что здесь может потребоваться выделение дополнительных блоков. Функция write map аналогична read map с той разницей, что, вместо поиска физического адреса блока по г-узлу и косвенным блокам, она добавляет новое (точнее, она добавляет номер новой зоны, а не блока).



Код функции write map сложный и длинный, поскольку эта функция должна учитывать несколько различных ситуаций. Если новая зона вставляется в начало файла, она должна быть вставлена в его г-узел.

Самый же худший случай, когда по мере роста файла простых косвенных блоков перестает хватать и приходится применять косвенные блоки второго уровня. После этого выделяется косвенный блок первого уровня, и его адрес заносится в блок второго. Как и в случае с чтением, для этого предусмотрена отдельная процедура, wrjndir. Если косвенный блок второго уровня выделить удалось и из-за этого диск переполнился, а на косвенный блок первого уровня места уже не хватает, то блок второго уровня нужно вернуть, чтобы избежать повреждения битовой карты.

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

Функция wrjndir записывает новый зонный в косвенный блок, а чтобы преобразовать данные в нужный формат (порядок следования байтов), она вызывает одну из подпрограмм преобразования, conv2 или conv4. Пусть имя этой функции не вводит вас в заблуждение. Помните, что действительную запись данных на диск выполняют функции, обслуживающие кэш блоков.

Далее в write.c следует функция clear zone. Она занимается очисткой блоков, оказавшихся в середине файла. Такое может случиться, если записать некоторое количество данных после конца файла. К счастью, это случается не очень часто.

Функция new block вызывается из rw chunk, когда требуется новый блок. На рис. 5.33 показаны шесть последовательных стадий увеличения файла. В этом примере размер блока равен 1 Кбайт, а зоны - 2 Кбайт.

Свободные зоны: 12 20 31 36..

24 25

Номер бпока

Рис. 5.33. Последовательное выделение блоков размером 1 Кбайт при размере зоны 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.
Копирование материалов разрешено исключительно при условии цититирования.