The design of the unix operating system by Maurice J
Вид материала | Реферат |
10.3 Терминальные драйверы |
- Лекция 10. Файловые системы Unix, 116.79kb.
- Уровни рассмотрения, 314.07kb.
- Курс по операционным системам (на примере ос windows) Основан на учебном курсе Windows, 29.21kb.
- Выполнил ученик 11 «А» класса, 443.51kb.
- Ос лекция 1 (2-й семестр – временно), 101.4kb.
- Operating System, 7686.97kb.
- Unix-подобные операционные системы, характеристики, особенности, разновидности, 40.63kb.
- 1. ms sql server. Общие сведения, 66.03kb.
- Shanti ananda maurice, 89.84kb.
- Методические материалы, 3002.45kb.
Дисковый драйвер транслирует адрес файловой системы, состоя-
щий из логического номера устройства и номера блока, в точный но-
мер дискового сектора. Драйвер получает адрес одним из следующих
путей: либо стратегическая процедура использует буфер из буферно-
го пула, заголовок которого содержит номера устройства и блока,
либо процедуры чтения и записи передают логический (младший) но-
мер устройства в качестве параметра; они преобразуют адрес смеще-
ния в байтах, хранящийся в пространстве задачи, в адрес соответс-
твующего блока. Дисковый драйвер использует номер устройства для
идентификации физического устройства и указания используемого
раздела, обращаясь при этом к внутренним таблицам для поиска сек-
тора, отмечающего начало раздела на диске. Наконец, он добавляет
номер блока в файловой системе к номеру блока, с которого начина-
ется каждый сектор, чтобы идентифицировать сектор, используемый
для ввода-вывода.
----------------------------------------------┐
│ Раздел Начальный блок Длина в блоках │
│ │
│ Размер блока = 512 байт │
│ │
│ 0 0 64000 │
│ 1 64000 944000 │
│ 2 168000 840000 │
│ 3 336000 672000 │
│ 4 504000 504000 │
│ 5 672000 336000 │
│ 6 840000 168000 │
│ 7 0 1008000 │
L----------------------------------------------
Рисунок 10.7. Разделы на диске RP07
Исторически сложилось так, что размеры дисковых разделов ус-
танавливаются в зависимости от типа диска. Например, диск DEC
RP07 разбит на разделы, характеристика которых приведена на Ри-
сунке 10.7. Предположим, что файлы "/dev/dsk0", "/dev/dsk1",
"/dev/dsk2" и "/dev/dsk3" соответствуют разделам диска RP07, име-
ющим номера от 0 до 3, и имеют аналогичные младшие номера. Пусть
размер логического блока в файловой системе совпадает с размером
дискового блока. Если ядро пытается обратиться к блоку с номером
940 в файловой системе, хранящейся в "/dev/dsk3", дисковый драй-
вер переадресует запрос к блоку с номером 336940 (раздел 3 начи-
нается с блока, имеющего номер 336000; 336000 + 940 = 336940) на
диске.
Размеры разделов на диске варьируются и администраторы распо-
лагают файловые системы в разделах соответствующего размера:
большие файловые системы попадают в разделы большего размера и т.
д. Разделы на диске могут перекрываться. Например, разделы 0 и 1
на диске RP07 не пересекаются, но вместе они занимают блоки с но-
мерами от 0 до 1008000, то есть весь диск. Раздел 7 так же зани-
мает весь диск. Перекрытие разделов не имеет значения, поскольку
файловые системы, хранящиеся в разделах, размещаются таким обра-
зом, что между ними нет пересечений. Иметь один раздел, включаю-
щий в себя все дисковое пространство, выгодно, поскольку весь том
можно быстро скопировать.
Использование разделов фиксированного состава и размера огра-
ничивает гибкость дисковой конфигурации. Информацию о разделах в
закодированном виде не следует включать в дисковый драйвер, но
нужно поместить в таблицу содержимого дискового тома. Однако,
найти общее место на всех дисках для размещения таблицы содержи-
мого дискового тома и сохранить тем самым совместимость с преды-
дущими версиями системы довольно трудно. В существующих реализа-
циях версии V предполагается, что блок начальной загрузки первой
из файловых систем на диске занимает первый сектор тома, хотя по
логике это, казалось бы, самое подходящее место для таблицы со-
держимого тома. И все же дисковый драйвер должен иметь закодиро-
ванную информацию о месте расположения таблицы содержимого тома
для каждого диска, не препятствуя существованию дисковых разделов
переменного размера.
В связи с тем, что для системы UNIX является типичным высокий
уровень дискового трафика, драйвер диска должен максимизировать
передачу данных с тем, чтобы обеспечить наилучшую производитель-
ность всей системы. Новейшие дисковые контроллеры осуществляют
планирование выполнения заданий, требующих обращения к диску, по-
зиционируют головку диска и обеспечивают передачу данных между
диском и центральным процессором; иначе это приходится делать
дисковому драйверу.
Сервисные программы могут непосредственно обращаться к диску
в обход стандартного метода доступа к файловой системе, рассмот-
ренного в главах 4 и 5, как пользуясь блочным интерфейсом, так и
не прибегая к структурированию данных. Непосредственно работают с
диском две важные программы - mkfs и fsck. Программа mkfs форма-
тирует раздел диска для файловой системы UNIX, создавая при этом
суперблок, список индексов, список свободных дисковых блоков с
указателями и корневой каталог новой файловой системы. Программа
fsck проверяет целостность существующей файловой системы и исп-
равляет ошибки, как показано в главе 5.
Рассмотрим программу, приведенную на Рисунке 10.8, в примене-
нии к файлам "/dev/dsk15" и "/dev/rdsk15", и предположим, что ко-
манда ls выдала следующую информацию:
ls -1 /dev/dsk15 /dev/rdsk15
br-------- 2 root root 0,21 Feb 12 15:40 /dev/dsk15
crw-rw---- 2 root root 7,21 Mar 7 09:29 /dev/rdsk15
Отсюда видно, что файл "/dev/dsk15" соответствует устройству
блочного типа, владельцем которого является пользователь под име-
нем "root", и только пользователь "root" может читать с него не-
посредственно. Его старший номер - 0, младший - 21. Файл
"/dev/rdsk15" соответствует устройству посимвольного ввода-выво-
да, владельцем которого является пользователь "root", однако пра-
ва доступа к которому на запись и чтение есть как у владельца,
так и у группы. Его старший номер - 7, младший - 21. Процесс, от-
крывающий файлы, получает доступ к устройству через таблицу клю-
-------------------------------------------------------------┐
│ #include "fcntl.h" │
│ main() │
│ { │
│ char buf1[4096], buf2[4096] │
│ int fd1, fd2, i; │
│ │
│ if (((fd1 = open("/dev/dsk5/", O_RDONLY)) == -1) || │
│ ((fd2 = open("/dev/rdsk5", O_RDONLY)) == -1))│
│ { │
│ printf("ошибка при открытии\n"); │
│ exit(); │
│ } │
│ │
│ lseek(fd1, 8192L, 0); │
│ lseek(fd2, 8192L, 0); │
│ │
│ if ((read(fd1, buf1, sizeof(buf1)) == -1) || │
│ (read(fd2, buf2, sizeof(buf2)) == -1)) │
│ { │
│ printf("ошибка при чтении\n"); │
│ exit(); │
│ } │
│ │
│ for (i = 0; i < sizeof(buf1); i++) │
│ if (buf1[i] != buf2[i]) │
│ { │
│ printf("различие в смещении %d\n", i); │
│ exit(); │
│ } │
│ printf("данные совпадают\n"); │
│ } │
L-------------------------------------------------------------
Рисунок 10.8. Чтение данных с диска с использованием блочного
интерфейса и без структурирования данных
чей устройств ввода-вывода блоками и таблицу ключей устройств по-
символьного ввода-вывода, соответственно, а младший номер
устройства 21 информирует драйвер о том, к какому разделу диска
производится обращение, например, дисковод 2, раздел 1. Поскольку
младшие номера у файлов совпадают, они ссылаются на один и тот же
раздел диска, если предположить, что это одно устройство (***).
Таким образом, процесс, выполняющий программу, открывает один и
тот же драйвер дважды (используя различные интерфейсы), позицио-
нирует головку к смещению с адресом 8192 и считывает данные с
---------------------------------------
(***) Не существует иного способа установить, что символьный и
блочный драйверы ссылаются на одно и то же устройство, кро-
ме просмотра таблиц системной конфигурации и текста про-
грамм драйвера.
этого места. Результаты выполнения операций чтения должны быть
идентичными при условии, что работает только одна файловая систе-
ма.
Программы, осуществляющие чтение и запись на диск непосредс-
твенно, представляют опасность, поскольку манипулируют с чувстви-
тельной информацией, рискуя нарушить системную защиту. Админист-
раторам следует защищать интерфейсы ввода-вывода путем установки
прав доступа к файлам дисковых устройств. Например, дисковые фай-
лы "/dev/dsk15" и "/dev/rdsk15" должны принадлежать пользователю
с именем "root", и права доступа к ним должны быть определены та-
ким образом, чтобы пользователю "root" было разрешено чтение, а
всем остальным пользователям и чтение, и запись должны быть зап-
рещены.
Программы, осуществляющие чтение и запись на диск непосредс-
твенно, могут также нарушить целостность данных в файловой систе-
ме. Алгоритмы файловой системы, рассмотренные в главах 3, 4 и 5,
координируют выполнение операций ввода-вывода, связанных с дис-
ком, тем самым поддерживая целостность информационных структур на
диске, в том числе списка свободных дисковых блоков и указателей
из индексов на информационные блоки прямой и косвенной адресации.
Процессы, обращающиеся к диску непосредственно, обходят эти алго-
ритмы. Пусть даже их программы написаны с большой осторожностью,
проблема целостности все равно не исчезнет, если они выполняются
параллельно с работой другой файловой системы. По этой причине
программа fsck не должна выполняться при наличии активной файло-
вой системы.
Два типа дискового интерфейса различаются между собой по
использованию буферного кеша. При работе с блочным интерфейсом
ядро пользуется тем же алгоритмом, что и для файлов обычного ти-
па, исключение составляет тот момент, когда после преобразования
адреса смещения логического байта в адрес смещения логического
блока (см. алгоритм bmap в главе 4) оно трактует адрес смещения
логического блока как физический номер блока в файловой системе.
Затем, используя буферный кеш, ядро обращается к данным, и, в ко-
нечном итоге, к стратегическому интерфейсу драйвера. Однако, при
обращении к диску через символьный интерфейс (без структурирова-
ния данных), ядро не превращает адрес смещения в адрес файла, а
передает его немедленно драйверу, используя для передачи рабочее
пространство задачи. Процедуры чтения и записи, входящие в состав
драйвера, преобразуют смещение в байтах в смещение в блоках и ко-
пируют данные непосредственно в адресное пространство задачи, ми-
нуя буферы ядра.
Таким образом, если один процесс записывает на устройство
блочного типа, а второй процесс затем считывает с устройства сим-
вольного типа по тому же адресу, второй процесс может не считать
информацию, записанную первым процессом, так как информация может
еще находиться в буферном кеше, а не на диске. Тем не менее, если
второй процесс обратится к устройству блочного типа, он автомати-
чески попадет на новые данные, находящиеся в буферном кеше.
При использовании символьного интерфейса можно столкнуться со
странной ситуацией. Если процесс читает или пишет на устройство
посимвольного ввода-вывода порциями меньшего размера, чем, к при-
меру, блок, результаты будут зависеть от драйвера. Например, если
производить запись на ленту по 1 байту, каждый байт может попасть
в любой из ленточных блоков.
Преимущество использования символьного интерфейса состоит в
скорости, если не возникает необходимость в кешировании данных
для дальнейшей работы. Процессы, обращающиеся к устройствам ввода
-вывода блоками, передают информацию блоками, размер каждого из
которых ограничивается размером логического блока в данной файло-
вой системе. Например, если размер логического блока в файловой
системе 1 Кбайт, за одну операцию ввода-вывода может быть переда-
но не больше 1 Кбайта информации. При этом процессы, обращающиеся
к диску с помощью символьного интерфейса, могут передавать за од-
ну дисковую операцию множество дисковых блоков, в зависимости от
возможностей дискового контроллера. С функциональной точки зре-
ния, процесс получает тот же самый результат, но символьный ин-
терфейс может работать гораздо быстрее. Если воспользоваться при-
мером, приведенным на Рисунке 10.8, можно увидеть, что когда
процесс считывает 4096 байт, используя блочный интерфейс для фай-
ловой системы с размером блока 1 Кбайт, ядро производит четыре
внутренние итерации, на каждом шаге обращаясь к диску, прежде чем
вызванная системная функция возвращает управление, но когда про-
цесс использует символьный интерфейс, драйвер может закончить
чтение за одну дисковую операцию. Более того, использование блоч-
ного интерфейса вызывает дополнительное копирование данных между
адресным пространством задачи и буферами ядра, что отсутствует в
символьном интерфейсе.
10.3 ТЕРМИНАЛЬНЫЕ ДРАЙВЕРЫ
Терминальные драйверы выполняют ту же функцию, что и осталь-
ные драйверы: управление передачей данных от и на терминалы. Од-
нако, терминалы имеют одну особенность, связанную с тем, что они
обеспечивают интерфейс пользователя с системой. Обеспечивая инте-
рактивное использование системы UNIX, терминальные драйверы имеют
свой внутренний интерфейс с модулями, интерпретирующими ввод и
вывод строк. В каноническом режиме интерпретаторы строк преобра-
зуют неструктурированные последовательности данных, введенные с
клавиатуры, в каноническую форму (то есть в форму, соответствую-
щую тому, что пользователь имел в виду на самом деле) прежде, чем
послать эти данные принимающему процессу; строковый интерфейс
также преобразует неструктурированные последовательности выходных
данных, созданных процессом, в формат, необходимый пользователю.
В режиме без обработки строковый интерфейс передает данные между
процессами и терминалом без каких-либо преобразований.
Программисты, например, работают на клавиатуре терминала до-
вольно быстро, но с ошибками. На этот случай терминалы имеют кла-
вишу стирания ("erase"; клавиша может быть обозначена таким обра-
зом), чтобы пользователь имел возможность стирать часть введенной
строки и вводить коррективы. Терминалы пересылают машине всю вве-
денную последовательность, включая и символы стирания (*** *). В
каноническом режиме строковый интерфейс буферизует информацию в
строки (набор символов, заканчивающийся символом возврата каретки
(*****)) и процессы стирают символы у себя, прежде чем переслать
исправленную последовательность считывающему процессу.
В функции строкового интерфейса входят:
* построчный разбор введенных последовательностей;
* обработка символов стирания;
* обработка символов "удаления", отменяющих все остальные сим-
волы, введенные до того в текущей строке;
* отображение символов, полученных терминалом;
* расширение выходных данных, например, преобразование символов
табуляции в последовательности пробелов;
* сигнализирование процессам о зависании терминалов и прерыва-
нии строк или в ответ на нажатие пользователем клавиши удале-
ния;
* предоставление возможности не обрабатывать специальные
символы, такие как символы стирания, удаления и возврата ка-
ретки.
---------------------------------------
(****) В этом разделе рассматривается использование терминалов
ввода-вывода, которые передают все символы, введенные
пользователем, без обработки.
(*****) В данной главе используется общий термин "возврат карет-
ки" для обозначения символов возврата каретки и перевода
строки.
Функционирование без обработки подразумевает использование
асинхронного терминала, поскольку процессы могут считывать симво-
лы в том виде, в каком они были введены, вместо того, чтобы
ждать, когда пользователь нажмет клавишу ввода или возврата ка-
ретки.
Ричи отметил, что первые строковые интерфейсы, используемые
еще при разработке системы в начале 70-х годов, работали в соста-
ве программ командного процессора и редактора, но не в ядре (см.
[Ritchie 84], стр.1580). Однако, поскольку в их функциях нуждает-
ся множество программ, их место в составе ядра. Несмотря на то,
что строковый интерфейс выполняет такие функции, из которых логи-
чески вытекает его место между терминальным драйвером и остальной
частью ядра, ядро не запускает строковый интерфейс иначе, чем че-
рез терминальный драйвер. На Рисунке 10.9 показаны поток данных,
проходящий через терминальный драйвер и строковый интерфейс, и
соответствующие ему управляющие воздействия, проходящие через
терминальный драйвер. Пользователи могут указать, какой строковый
интерфейс используется посредством вызова системной функции
ioctl, но реализовать схему, по которой одно устройство использо-
вало бы несколько строковых интерфейсов одновременно, при чем
каждый интерфейсный модуль, в свою очередь, успешно вызывал бы
следующий модуль для обработки данных, довольно трудно.
Поток данных Поток управляющих
воздействий
------------------------┐ ------------------------┐
│ Процесс чтения/записи │ │ Процесс чтения/записи │
L------------------------ L------------------------
│ │
v │ v │
----------------------┐ ------------------------┐
вывод │ Строковый интерфейс│ ввод │ Терминальный драйвер │
L---------------------- L------------------------
│ │
v │ v │
------------------------┐ ----------------------┐
│ Терминальный драйвер │ │ Строковый интерфейс │
L------------------------ L----------------------
│
v │
------------------------┐
│ Драйвер ввода-вывода │
L------------------------
│
v │
--------------------------┐
│ Устройство ввода-вывода │
L--------------------------
Рисунок 10.9. Последовательность обращений и поток данных че-
рез строковый интерфейс
Указатель Смещение Смещение
на до до
следующий начала конца Массив символов
блок 0 1 2 3 4 5 6 7 8 9 14
----------T---------T---------TT-T-T-T-T-T-T-T-T-T-T-T-T-T-T-T---
│ 7 │ 14 ││g│a│r│b│a│g│e│││ │e│q│n│ │││ │...
----+-----+---------+---------++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+---
│
v
Рисунок 10.10. Символьный блок
10.3.1 Символьные списки
Строковый интерфейс обрабатывает данные в символьных списках.
Символьный список (clist) представляет собой переменной длины
список символьных блоков с использованием указателей и с подсче-
том количества символов в списке. Символьный блок (cblock) содер-
жит указатель на следующий блок в списке, небольшой массив храни-
мой в символьном виде информации и адреса смещений, показывающие
место расположения внутри блока корректной информации (Рисунок
10.10). Смещение до начала показывает первую позицию расположения
корректной информации в массиве, смещение до конца показывает
первую позицию расположения некорректной информации.
Ядро обеспечивает ведение списка свободных символьных блоков
и выполняет над символьными списками и символьными блоками шесть
операций.
1. Ядро назначает драйверу символьный блок из списка свободных
символьных блоков.
2. Оно также возвращает символьный блок в список свободных сим-
вольных блоков.
3. Ядро может выбирать первый символ из символьного списка: оно
удаляет первый символ из первого символьного блока в списке и