Лекции Системное ПО. Лекция Структура и основные компоненты вычислительной системы
Скачать 0.71 Mb.
|
Лекция 8 Индексные Дескрипторы Рассмотрим подробнее Индексные Дескрипторы. ИД — это объект Unix, который ставится во взаимнооднозначное соответствие с содержимым файла. То есть для каждого ИД существует только одно содержимое и наоборот, за исключением лишь той ситуации, когда файл ассоциирован с каким-либо внешним устройством. Напомним содержимое ИД: поле, определяющее тип файла (каталоги и все остальные файлы); код привилегии/защиты; количество ссылок к данному ИД из всевозможных каталогов файловой системы; (нулевое значение означает свободу ИД) длина файла в байтах; даты и времена (время последней записи, дата создания и т.д.); поле адресации блоков файла. Как видно — в ИД нет имени файла. Давайте посмотрим, как организована адресация блоков, в которых размещается файл. В поле адресации находятся номера первых десяти блоков файла, то есть если файл небольшой, то вся информация о размещении данных файла находится непосредственно в ИД. Если файл превышает десять блоков, то начинает работать некая списочная структура, а именно, 11й элемент поля адресации содержит номер блока из пространства блоков файлов, в которых размещены 128 ссылок на блоки данного файла. В том случае, если файл еще больше — то используется 12й элемент поля адресации. Сутьего в следующем — он содержит номер блока, в котором содержится 128 записей о номерах блоках, где каждый блок содержит 128 номеров блоков файловой системы. А если файл еще больше, то используется 13 элемент — где глубина вложенности списка увеличена еще на единицу. Таким образом мы можем получить файл размером (10+128+1282+1283)*512. Если мы зададим вопрос — зачем все это надо (таблицы свободных блоков, ИД и т.д.), то вспомним, что мы рассматриваем взаимосвязь между аппаратными и программными средствами вычислительной системы, а в данном случае подобное устройство файловой системы позволяет сильно сократить количество реальных обменов с ВЗУ, причем эшелонированная буферизация в ОС Unix делает число этих обменов еще меньше. Рассмотрим следующую область — область сохранения. На схеме она изображена сразу за блоками файлов. На самом же деле она может размещаться по-разному: перед блоками файлов, в каком-нибудь файле или еще где-нибудь, например, на другом ЗУ. Все это зависит от конкретной реализации системы. В область сохранения происходит откачка процессов, она же используется для оптимизации запуска наиболее часто запускающихся процессов (использование так называемого T-бита файла). Мы с вами рассмотрели структуру файловой системы и ее организацию на системном устройстве. Эта структура и алгоритмы работы с ней достаточно простые, это сделано для того, чтобы накладные расходы, связанные с функционированием системы, не выходили за пределы разумного. Элементы файловой системы: Каталоги Мы говорили, что вся информация в Unix размещается в файлах. Нету каких-то специальных таблиц, которые размещены вне файловой системы и используются системой, за исключением тех таблиц, которые создает ОС во время работы в пространстве оперативной памяти. Каталог с точки зрения ОС — это файл, обычный файл, в котором размещены данные о всех файлах, которые принадлежат каталогу. Мы говорим, что в каталоге “А” содержатся файлы: “B”, “C” и “D” — из которых “В” и “С” могут быть как файлами, так и каталогами, а “D” — заведомо каталог. Каталог имеет следующую структуру. Он состоит из элементов, объединяющих в себе два поля — номер ИД и имя файла: Каталог = { {ИД, Имя}, {ИД,Имя}, ..., {ИД, Имя}} Что есть номер ИД? — это порядковый номер элемента в списке индексных дескрипторов. Так, первый элемент этого списка — ИД#1 принадлежит корневому каталогу “.”. В общем случае, в каталоге могут неоднократно встречаться записи, ссылающиеся на один и тот же ИД, но в каталоге не могут встречаться записи с одинаковыми именами. То есть с содержимым файла может быть связано произвольное количество имен. При создании каталога в нем всегда создаются две записи: {ИД_самого_каталога, “.”} и {ИД_родительского_каталога, “..”} Так на картинке файл “А” имеет ИД#7, “D” — ИД#5, “F” — ИД#10, “G” — ИД#101. В этом случае файл-каталог D будет иметь следующее содержимое: {{ 5, “.” }, { 7, “..”}, {10, “F”}, {101,”G”}} (Для корневого каталога родитель ссылается на него же самого.) Чем отличается файл-каталог от обычного файла? Он отличается полем типа в индексном дескрипторе. Давайте посмотрим, как схематично могут использоваться полные имена и ссылки на каталоги. В системе в каждый момент времени определен для пользователя текущий каталог. То есть каталог, полное имя которого подставляется ко всем файлам, имя которых не начинается с символа “/”. Если текущий каталог “D”, то можно говорить просто о файле “F” или файле “G”, если же текущий каталог “D”, а требуется добраться до файла “B”, то оперировать просто с именем“B” нельзя, так как он не принадлежит каталогу “D”, файл “B” можно достать, указав его полное имя от корня, либо использовать специальный файл “..”, в этом случае файл “B” будет иметь имя: “../B”. Если при открытии мы ссылаемся на “..” Для того, чтобы в этом случае открыть файл “B”, придется выполнить ряд косвенных операций — взять ИД родитель, и по нему выбирается содержимое файла-каталога “А”, в “А” мы выбираем строку с именем “B” и определяем его ИД. Эта процедура достаточно трудоемка, но так как открытие и закрытие файлов происходит достаточно редко, то “криминала” в этом никакого нету. За счет такой организации каталогов у нас содержимое файла разорвано с его именем. Имя может быть определено неоднозначно. Так как с одним файлом может ассоциировано несколько имен, то можно говорить о том, что этот файл может быть одновременно открыт несколькими процессами (вообще говоря, имея одно имя мы тоже можем открыть этот файл из нескольких процессов, суть проблемы от этого уточнения не изменяется). Как организуется синхронизация в этом случае? Как мы увидим позже, здесь все решается корректно. Файлы устройств Эта разновидность файлов характеризуется типом и их интерпретация происходит следующим образом. В принципе, содержимого у файлов устройств нету, то есть это лишь ИД и имя, которое с ним ассоциировано. В ИД указывается информация о том, какой тип устройства ассоциирован с этим файлом, соответственно, система Unix все устройства подразделяет на два типа: байт- и блок-ориентированные. Байт-ориентированные устройства — это те устройства, обмен с которыми происходит по байтам (например, клавиатура), блок-ориентированные — это такие устройства, обмен с которыми происходит блоками. В ИД имеется поле, указывающее эту характеристику, там же имеется поле, определяющее номер драйвера, связанного с этим устройством. В системе каждый драйвер связан с конкретным одним устройством, но у устройства может быть несколько драйверов. Это поле, определяющее номер драйвера, на самом деле есть номер в таблице драйверов соответствующего класса устройств (имеются две таблицы — для блок- и байт- устройств). Также в ИД существует некоторый цифровой параметр, который может быть передан драйверу в качестве параметра, уточняющего информацию о работе. Это то, что можно сказать о специальных файлах, связанных с внешними устройствами. Обмен данными с файлами Следующее из системной организации файловой системы — это организация обменом данными с файлом. Определим понятия, связанные с низкоуровневым вводом/выводом. В Unix определены специальные функции, которые называются системными вызовами. Эти вызовы осуществляют непосредственное обращение к ОС, они выполняют некоторые системные функции. По употреблению они практически не отличаются от использования библиотечных функций, тогда как по реализации и действии их отличие достаточно существенное. Библиотечная функция будет загружена в тело процесса, а системный вызов сразу передает управление ОС, и последняя выполняет заказанное действие. В Unix для обеспечения низкоуровневого (путем системных вызовов) ввода-вывода имеется набор этих функций: open(...) — для работы с содержимым файла процесс должен зарегистрировать в системе этот факт, параметрами этой функции являются строка, содержащая имя файла и атрибуты на режим работы с файлом (только чтение, чтение-запись и т.п.), а возвращает эта функция некоторое число, которое называется файловым дескриптором (ФД). В теле процесса пользователя, а также данных, ассоциированных с этим процессом, размещается некая служебная информация. В частности, размещается таблица файловых дескрипторов. Она, как и все таблицы в Unix — позиционна, то есть номер строки в таблице соответствует ФД с этим номером. С ФД ассоциировано имя файла и прочие атрибуты. Нумерация ФД — прерогатива процесса, то есть ФД уникальны в пределах одного процесса. Количество одновременно открытых файлов (точнее, максимальное количество ФД, ассоциированных с файлами) для процесса регламентируется системой. Итак, функция open(...) — открытие существующего файла. creat(...) — это функция открытия нового файла, ее параметрами служат: имя файла и некоторые параметры открытия, также как и у open. read(...)/write(...) — их параметрами являются номер ФД и некоторые параметры доступа. Эти функции служат для чтение/записи из или в файл. close(...) — завершение работы с файлом. После выполнения этой функции ФД этого файла освобождается. Все это системные вызовы. Также в Unix можно осуществлять ввод-вывод через библиотечные функции (например, fopen, fread, fwrite, fclose, ...). Рассмотрим организацию обмена с системной точки зрения в Unix. При организации обмена система подразделяет все данные на две категории — первая, это данные, ассоциированные с процессом пользователя, и данные, ассоциированные с ОС. Первая таблица данных, связанных с ОС — это таблица индексных дескрипторов открытых файлов (ТИДОФ), эта таблица содержит записи, каждая из которых содержит копию ИД для каждого открытого в системе файла. Через копию ИД мы осуществляем доступ к блокам файла. Каждая из этих записей содержитполе, характеризующее количество открытых файлов в системе, использующих данные ИД. То есть, если мы открываем один и тот же файлов от имени двух процессов, то запись в ТИДОФ создается одна, но каждое открытие этого ИД увеличивает счетчик на единицу. Следующее. Таблица файлов — эта таблица содержит информацию об имени открытого файла и имеет ссылку на ИД данного файла в ТИДОФ. Подробнее эта схема будет рассмотрена на следующей лекции. Лекция 9 Мы начали рассмотрение принципов организации работы ОС Unix с файловой системы. Точнее организации обработки ввода-вывода. Теперь давайте посмотрим, как организуется обработка низкоуровневого обмена с точки зрения ОС. Понятно, что мы будем рассматривать чуть более общую модель и не уделять времени не самым значащим деталям. Для поддержания ввода-вывода в системе все данные в системе подразделяются на два типа: общесистемные данные (ТИДОФ, например). Размер ТИДОФ фиксирован — это еще один параметр настройки системы. Каждая запись этой таблицы содержит некоторую информацию, из которой нас будет интересовать следующее: Копия ИД открытого файла. То есть для любого открытого файла ИД, который характеризует содержимое этого файла, копируется и размещается в ТИДОФ. После этого все манипуляции с файлами происходят через копию ИД. Не с ИД, который на диске, а с его копией. Таким образом доступ к информации осуществляется оперативно. Счетчик открытых в данный момент файлов, связанных в данный момент с данным ИД. Это означает, что на любое количество открытий файла, связанного с данным ИД, система работает с одной копией этого ИД. Рассмотрим следующую таблицу — Таблицу Файлов (ТФ) — она также состоит из определенного числа записей. Каждая запись в ТФ соответствует открытому в системе файлу. При этом в подавляющем большинстве случаев это есть взаимооднозначное соответствие (исключения мы рассмотрим позже). Каждая запись ТФ содержит указатели чтения-записи по файлу. Это означает, что если мы открыли один и тот же файл в двух или в одном процессе, то с каждым таким открытием связано по одному указателю, и они друг с другом независимы, то есть если мы изменяем один указатель, то другой остается в прежнем состоянии. И так почти всегда, за исключением некоторых случаев. Кроме того, в строке ТФ содержится некоторая запись — число, которое называется индексом наследственности. Это данные уровня ОС, то есть данные, которые описывают состояние системы в целом. С каждым процессом связана так называемая таблица открытых файлов (ТОФ). Номер записи в данной таблице — номер файлового дескриптора, каждая строка имеет ссылку на соответствующую строку ТФ. Это означает, что информация об указателях как бы разорвана. То есть файловый дескриптор, являющийся атрибутом процесса, с другой стороны является атрибутом ОС. Для того, чтобы мы имели возможность рассмотреть все грани данного вопроса — забежим немного вперед и рассмотрим некоторые вещи, связанные с формированием процесса. ОС Unix имеет функцию, которая называется fork(...) — это системный вызов, при обращении к которому происходит некоторое “бесполезное” действие — создается процесс двойник — полная (с некоторыми замечаниями) копия процесса, в котором встретилась эта функция. Для чего это нужно? Нужно, для чего — увидим позже. При формировании процесса двойника есть две особенности: процесс-сын имеет все те же файлы открытыми, что были открыты в процессе отца; система позволяет идентифицировать — где сын, где отец; Предположим, у нас есть процесс #1 и с ним ассоциирована ТОФ1, в этом процессе открыт файл с именем “name”, ему соответствует ФД i. Это означает, что строка i ТОФ будет иметь ссылку на строку из ТФ, где содержится системная информация о файле, в том числе указатели чтения/записи. Записи в ТФ имеют ссылку на строку ТИДОФ, где находится копия ИД файла с именем “name”. Предположим, что в этом же процессе мы открыли еще раз файл с именем “name”. Система поставила ему в соответствие ФД j, это означает, что в записи j ТОФ будет ссылка на запись в ТФ, соответствующую второму открытию файла “name”.Индексы наследственности в обоих случаях будут равны единице. Соответственно, когда мы изменяем указатель файла i (читаем или пишем), то файловый указатель j изменяться не будет. Обе записи в ТФ ссылаются на один и тот же ИД файла. Теперь предположим, что процесс #1 выполнил обращение к функции fork(...). Образовалась копия этого процесса, причем обе копии начинают работать на выходе из процесса. И, соответственно, со вторым процессом будет ассоциирована ТОФ2. Файлы “name” с дескрипторами i,j будут также открыты во втором процессе. Но, когда процесс получает открытые файлы в наследство от родителя, то ссылки из соответствующих строк таблицы ТОФ будут происходить не на новые строки ТФ, а на те же самые, на которые ссылались ФД родителя. Это означает, что у обоих процессов будут одинаковые указатели файлов — при перемещении указателя для ФД i в процессе-отце будет также изменен файловый указатель для ФД i в сыне и наоборот. Вообще говоря, два процесса по ФД i (или любому переданному в наследство открытому файлу) будут иметь общий указатель файла. Вот это случай, когда нет взаимооднозначного соответствия между строками ТФ и ТОФ. И во время создания процесса сына счетчик наследственности увеличивается на единицу. Что означает такая организация доступа к данным файла? Это означает, что этот доступ осуществляется централизованно, то есть в конечном итоге все заказы на обмен идут через одну единственную запись, сколько бы раз файл ни был открыт в системе. Отсюда мы получаем отсутствие путаницы при доступе к файлу. При любом формировании нового процесса, система автоматически связывает 0, 1 и 2 ФД с предопределенными файлами: 0 — системный файл ввода (обычно — файл устройства клавиатура); 1 — системный файл вывода (обычно — файл устройства монитор); 2 — файл вывода диагностических сообщений (обычно — также файл устройства монитор). Рассмотрим типовые действия при обращении к тем или иными системным вызовам. При обращении к функции fork(...) система создает копию процесса и дублирует ТОФ родителя в ТОФ сыновнего процесса, а также увеличивает на единицу индексы наследственности в соответствующих строках ТФ, и увеличивает счетчик связей в ТИДОФ. При выполнении системного вызова open(...): По полному имени определяется каталог, в котором размещается файл; Определяется номер ИД файла; По номеру ИД осуществляется поиск в ТИДОФ, если запись с данным номером обнаружена, то номер соответствующей строки ТИДОФ фиксируется и переходим к шагу 5; Если такой записи не обнаружено, происходит формирование новой строки в ТИДОФ, соответствующей новому ИД и фиксируется ее номер; Корректируется счетчик ссылок (количество открытых файлов, использующих данный ИД) в ТИДОФ. Номер строки ТИДОФ записывается в строку ТФ, а ее номер возвращается в ТОФ; При операциях ввода-вывода мы идем по ссылкам и добираемся до нужного блока данных. Взаимодействие с устройствами. Мы говорили, что все устройства, которые обслуживает ОС Unix, могут быть подразделены на два типа: байт- и блок-ориентированные. С первыми устройствами все обмены осуществляются порциями по одному байту, с остальными — некоторыми порциями байт. С точки зрения ОС одно и то же устройство может рассматриваться как байт- и как блок-ориентированное. Примером такого устройства может быть оперативная память. Различие составляет наличие или отсутствие соответствующих драйверов, ибо существует две таблицы — байт- и блок-ориентированных драйверов. На эти таблицы имеются ссылки в ИД специальных файлов. Основной особенностью организации работы с блок-ориентированными устройствами является возможность буферизации обмена. В оперативной памяти организован пул буферов.
Каждый из буферов пула состоит из буфера размером один блок. Каждый из этих блоков может быть ассоциирован с драйвером одного из блок-ориентированных устройств. Посмотрим, какие происходят действия при выполнения заказа на чтение блока. Пусть поступил заказ на чтение Nного блока из устройства с номером M. Тогда: Среди буферов пула ищется содержащий Nный блок Mмого устройства. Если он найден, то фиксируется номер этого буфера (следует отметить, что в этом случае реального обращения к устройству нет, а чтением информации является предоставление информации из найденного буфера) и переходим на четвертый шаг; Если поиск оказался неудачным, то в пуле осуществляется поиск буфера для чтения и размещения содержимого данного блока. Если есть свободный буфер (реально такая ситуация может быть только при старте системы), то фиксируем его номер и переходим к пункту 3. Если же свободного буфера нет, то выбирается буфер, обращений к которому не было самое длительное время. Если для него имеется установленный признак записи информации в буфер (при последнем обращении была произведена запись) — происходит запись информации из буфера на физическое устройство (если признака записи не было, то просто игнорируем содержимое), и, фиксируя номер, переходим к шагу 3. Осуществляется чтение Nного блока устройства M в найденный буфер. Происходит обнуление счетчика времени для данного буфера, а счетчики времени всех остальных буферов пула увеличиваем на единицу. Передаем в качестве результата содержимое буфера. Это последовательность действий, связанных с операцией чтения блока. Мы видим, что здесь есть элемент оптимизации связанный с количеством реальных обращений к физическому устройству. Запись блоков осуществляется по аналогичной схеме. Очевидны преимущества, недостатком же является то, что система в случае буферизации является критичной к несанкционированным выключениям системы. То есть в ситуации, когда буфера системы не выгружены, а происходит нештатное завершение программ ОС, может произойти потеря данных. Другой недостаток — то, что при буферизации разорваны во времени обращения к системе за обменом и реальные обмены. Этот недостаток проявляется в том, что если при записи в буфер система возвращает процессу результат, что запись прошла успешно, а при реальном обмене с физическом устройстве происходит сбой — эта ситуация плоха. Для борьбы с вероятностью потери информации во время появления внештатных ситуаций система действует следующим образом — в ОС есть некоторый параметр, который определяет периоды времени, через которые происходит сброс буферов. Второе — имеется команда, которая может быть доступна пользователю — команда sync() — по этой команде осуществляется сброс данных на диск. И третье — система обладает некоторое избыточностью, позволяющей в случае потери информации произвести набор действий, которые эту информацию восстановят полностью. Если же для некоторых блоков принадлежность к файлу установить не получается, то эти блоки будут записаны в отдельные файлы. Но на самом деле с развитием ОС и аппаратуры фатальные потери информации встречаются редко. Мы начинали разговор о том, что существуют системные вызовы, а существуют библиотеки ввода-вывода. Библиотеки ввода-вывода также позволяют оптимизировать работу системы. Рассмотрим стандартную библиотеку stdio.h. Концептуальная суть обменов через нее такая же, как и через системные вызовы. Но если open возвращает номер ФД, то fopen возвращает указатель на некоторую структуру — это первое. Второе и основное — многие функции сервиса, которые предоставляет библиотека реализуются внутри адресного пространства процесса, в частности такой функцией сервиса является еще один уровень буферизации ввода-вывода. Суть его в том, что на ресурсах процесса можно выделить буфер, который будет работать аналогично буферному пулу ОС и будет минимизировать обращение процесса к системным вызовам ввода-вывода. Двойная буферизация, очевидно, вещь полезная — если мы обращаемся за обменом, например, на полблока, то буфер в процессе соберет эти половинки и системный вызов будет запрашивать уже обмен с целым блоком. Что невыгодно? То, что буферизация существует в пределах адресного пространства процесса, и мы теряем всякую синхронизацию между процессами по обмену с общим открытым файлом, так как в каждом процессе может быть свой внутренний буфер. Но тем не менее стандартная библиотека ввода-вывода удобна. А уж пользователь берет на себя проблемы синхронизации, да и сама библиотека позволяет блокировать внутреннюю буферизацию. |