Академический Документы
Профессиональный Документы
Культура Документы
СИСТЕМЫ
Разработка и реализация
3-е издание
Э. ТАНЕНБАУМ, А. ВУДХАЛЛ
ОПЕРАЦИОННЫЕ
СИСТЕМЫ
Разработка и реализация
3-е издание
ПИТЕР
Москва • Санкт-Петербург ■ Нижний Новгород • Воронеж
Новосибирск • Ростов-на-Дону ■ Екатеринбург ■ Самара
Киев ■ Харьков ■ Минск
2007
ББК 32.973-018.2
УДК 004.451
Т18
ББК 32.973-018.2
УДК 004.451
Texty за лучший учебник. А в 2005 году Э. Таненбаум стал одним из пяти новых
профессоров Королевской Академии (Royal Academy). Его домашняя страница
в Интернете расположена по адресу http://www.cs.vu.nl/~ast/ .
Альберт Вудхалл (Albert S. Woodhull) получил степень бакалавра в Массачу
сетсском технологическом университете и степень доктора в университете Ва
шингтона. Поступив в Массачусетсский институт, чтобы стать электротехником,
он окончил его как биолог. Сам себя он называет «ученым, неплохо разбираю
щимся в технике». Более 20 лет он был преподавателем Школы естественных
наук Хэмпширского колледжа, Массачусетс, преподавая параллельно в несколь
ких других колледжах и университетах. Как биолог, пользующийся электрон
ным оборудованием, он начал работать с микрокомпьютерами, когда они стали
доступными. Его технические курсы для студентов развились в лекции, посвя
щенные взаимодействию и программированию задач реального времени.
Доктор Вудхалл всегда испытывал большой интерес к преподаванию и к вопро
сам влияния науки и технологии на производство. Перед поступлением в аспи
рантуру он в течение двух лет преподавал естественные науки в Нигерии. Позже
он потратил несколько своих отпусков на обучение студентов вычислительной
технике в Никарагуа, в Universidad National de Ingenieria и Universidad National
Autonoma de Nicaragua.
В сферу его интересов входят компьютеры как электронные системы и взаимо
действие компьютеров с другими электронными системами. Он особенно насла
ждается преподаванием архитектуры вычислительной техники, операционных
систем и компьютерных коммуникаций, программирования на языке ассемблер.
А. Вудхалл также работал консультантом по разработке электронного оборудо
вания и связанного с ним программного обеспечения, а также системным адми
нистратором.
Помимо этого у него немало других, не академических интересов, включая спор
тивные игры на открытом воздухе, радиолюбительство и чтение. Он любит путеше
ствовать и изучать другие языки помимо родного английского. Вудхалл являет
ся пользователем и горячим сторонником системы MINIX. Его страничка в Сети
управляется MINIX и располагается по адресу http://minix1.hampshire.edu/asw/ .
Сюзанне, Барбаре, Марвину, памяти моих дорогих л и Брэма.
Э. Таненбаум
Барбаре и Гордону.
А. Вудхалл
Предисловие
Большинство книг, посвященных операционным системам, в основном касаются
теории, а не практики. Та же, которую вы держите в руках, в этом смысле более
сбалансирована. В ней скрупулезно рассматриваются все теоретические основы,
в том числе процессы, взаимодействие между процессами, семафоры, мониторы,
передача сообщений, планирование, ввод-вывод, взаимные блокировки, драйве
ры устройств, управление памятью, замещение страниц, разработка файловых
систем, безопасность и защита данных. В то же время обсуждается конкретная
UNIX-совместимая операционная система MINIX и приводится копия ее исход
ных кодов (на компакт-диске). Это позволяет не только изучать основополагаю
щие принципы, но и видеть, как эти принципы применяются в реальных опера
ционных системах.
Появившись в 1987 году, первая редакция этой книги в определенной степени
произвела революцию в понимании того, как нужно изучать операционные сис
темы. До того большинство книг посвящалось только теоретической части. С по
явлением MINIX во многих школах стали проводить лабораторные занятия, на
которых ученики могли «изнутри» увидеть, как работают операционные систе
мы. Мы сочли эту тенденцию весьма желательной и надеемся, что она сохранит
ся и в третьей редакции.
За первые десять лет операционная система MINIX претерпела множество из
менений. Первоначальный код был рассчитан на IBM PC с процессором 8088
и 256 Кбайт памяти с двумя дисководами, но без жестких дисков. В основе
MINIX лежала система UNIX версии 7. С течением времени система MINIX
развивалась в различных направлениях: появилась поддержка компьютеров с 32-
разрядным защищенным режимом, оснащенных оперативной памятью и жесткими
дисками большого объема. Кроме того, система теперь базируется не на UNIX
версии 7, а на международном стандарте POSIX (IEEE 1003.1 и ISO 9945-1).
Добавлено множество новых возможностей — на наш взгляд, пожалуй, даже
слишком много. (Впрочем, некоторым и этого мало, что в конце концов и привело
к появлению Linux.) В дополнение, система MINIX была перенесена на множе
ство других платформ, включая Macintosh, Amiga, Atari и SPARC. Вторая редак
ция данной книги, в которой рассматривалась именно эта версия MINIX, вышла
в свет в 1997 году и широко использовалась в университетах.
Операционная система MINIX сохраняет свою популярность, о чем свидетельст
вует количество запросов по слову MINIX в поисковой системе Google.
Предисловие 15
От издателя перевода
Ваши замечания, предложения и вопросы отправляйте по адресу электронной
почты comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
Все исходные тексты, приведенные в книге, вы можете найти по адресу http://
www.piter.com/download.
Подробную информацию о наших книгах вы найдете на веб-сайте издательства:
http://www.piter.com.
Глава 1
Введение
Без программного обеспечения любой компьютер — просто бесполезная груда
железа. Именно благодаря программам компьютер может хранить, обрабатывать
и искать информацию, воспроизводить музыку и видео, отсылать сообщения
электронной почты, вести поиск в Интернете и решать множество других важных
задач, для которых он и предназначен. Программное обеспечение можно грубо
разбить на две большие группы: системные программы, управляющие работой
самого компьютера, и прикладные программы, предназначенные для решения поль
зовательских задач. Самая главная системная программа — это операционная
система, она управляет всеми системными ресурсами и обеспечивает основу для
работы прикладных программ. Именно операционные системы являются пред
метом рассмотрения в данной книге. В качестве примера, демонстрирующего
принципы архитектуры и их практическую реализацию, приведена ОС MINIX 3.
Современный компьютер состоит из одного или нескольких процессоров, опера
тивной памяти, дисков, клавиатуры, монитора, принтеров, сетевых интерфейсов
и других устройств ввода-вывода, то есть является сложной системой. Написание
программ, которые отслеживают все компоненты, корректно используют их и при
этом оптимально работают, представляет собой крайне трудную задачу. Если бы
каждому программисту приходилось задумываться о том, как работают жесткие
диски, помнить о множестве нюансов, которые могут произойти при чтении блока
данных, то многие программы, скорее всего, вообще не были бы написаны.
Еще много лет назад стало очевидно, что нужно как-то оградить программистов
от тонкостей, связанных с аппаратным обеспечением. Постепенно был вырабо
тан следующий путь: поверх аппаратуры работает дополнительная программная
прослойка, которая управляет всем оборудованием и предоставляет пользова
телю интерфейс, или виртуальную машину, более простую для понимания и про
граммирования, чем аппаратура. Операционная система и является этой про
граммной прослойкой.
Место операционной системы в общей структуре компьютера показано на ри
сунке 1.1. Внизу находится аппаратное обеспечение, которое во многих случаях
само состоит из двух или более уровней (или слоев). Самый нижний уровень
содержит физические устройства, состоящие из интегральных микросхем, про
водников, источников питания, электронно-лучевых трубок и т. п. То, как они
устроены и как работают, относится к сфере деятельности инженеров, специали
стов по электронике.
Введение 19
Машинный язык
Физические устройства
файла в этом случае представляется намного более простым действием, чем в слу
чае, когда программисту приходится думать о перемещении головок диска, о за
держках, связанных с их установкой в нужное место и т. д.
Поверх операционной системы на нашем рисунке расположены остальные сис
темные программы. Здесь находятся интерпретатор команд (оболочка), ком
пиляторы, редакторы и т. д. Важно понимать, что подобные программы не яв
ляются частью операционной системы, хотя обычно поставщики компьютеров
устанавливают их на машины. Это очень важное, хотя и тонкое, замечание. Под
операционной системой обычно понимается то программное обеспечение, кото
рое запускается в режиме ядра или, как его еще называют, режиме супервизора.
Операционная система защищена от вмешательства пользователя с помощью
аппаратных средств (мы не рассматриваем в данный момент некоторые старые
микропроцессоры, которые вообще не имеют аппаратной защиты). Компилято
ры и редакторы запускаются в пользовательском режиме. Если пользователю не
нравится какой-либо компилятор, он при желании может написать собственный,
но ему не удастся написать собственный обработчик прерываний от системных
часов, являющийся частью операционной системы и обычно защищенный аппа
ратно от попыток его модифицировать.
Подобная классификация имеет весьма размытые границы во встраиваемых сис
темах, допускающих отсутствие ядра, и в интерпретируемых системах (к приме
ру, в Java-системах, где компоненты разделяются путем интерпретации, а не ап
паратно). Тем не менее в традиционных компьютерах операционная система
является элементом, исполняемым в режиме ядра.
Во многих системах применяются программы, хотя и исполняемые в пользо
вательском режиме, но призванные помогать операционной системе или ре
шать привилегированные задачи. Распространенный пример — программа сме
ны пароля пользователями. Она не является частью операционной системы и не
работает в режиме ядра, однако несет важную функцию и, очевидно, требует осо
бой защиты.
В некоторых системах, включая MINIX 3, описанный подход реализован столь
утрированно, что компоненты, традиционно относимые к операционной системе
(к примеру, файловая система), функционируют в пользовательском режиме. Гра
ница между системным и прикладным программным обеспечением размывается.
Очевидно, что компоненты, исполняемые в режиме ядра, относятся к операцион
ной системе, однако многие «пользовательские» программы также несут систем
ные функции либо, как минимум, тесно связаны с ними. К примеру, в MINIX 3
файловая система представляет собой не что иное, как большую С-программу,
исполняемую в пользовательском режиме.
Подведем итог вышесказанному: поверх системных программ выполняются при
кладные программы. Обычно они покупаются пользователем (или пишутся им)
для решения собственных проблем — обработки текста, электронных таблиц,
технических расчетов или хранения информации в базе данных.
1.1. Понятие операционной системы 21
очень хорош для считывания карт, копирования лент и печати выходных дан
ных, но не подходил для числовых вычислений.
Другие, более дорогостоящие машины, такие как IBM 7094, использовались для
настоящих вычислений (рис. 1.2).
Рис. 1.2. Ранняя система пакетной обработки: а — программист приносит карты для IBM 1401;
б — IBM 1401 записывает пакет заданий на магнитную ленту; в — оператор приносит входные
данные на ленте к IBM 7094; г — IBM 7094 выполняет вычисления; д — оператор переносит
ленту с выходными данными на IBM 1401; е — IBM 1401 печатает выходные данные
Программа на Фортране
Разделы
памяти
Мысль о том, что машины, гораздо более мощные, чем их мэйнфрейм GE-645,
будут продаваться миллионами по цене тысяча долларов за штуку всего лишь
через тридцать лет, казалась чистейшей научной фантастикой, как если бы сего
дня кто-либо вздумал проектировать сверхзвуковые трансатлантические подвод
ные поезда.
Успех MULTICS не был полным. Предполагалось, что система сможет обслужи
вать сотни пользователей, будучи лишь немногим мощнее персональных компь
ютеров на базе Intel 80386 (хотя система MULTICS значительно превосходила
их в объеме ввода-вывода). Идея была не столь безумна, как кажется, поскольку
в то время люди умели писать компактные и эффективные программы (похоже,
впоследствии это умение было утрачено). Системе MULTICS не удалось поко
рить мир в силу целого ряда причин, весьма важной из которых является ис
пользование языка PL/I для ее создания. Компилятор PL/I появился с опозда
нием на несколько лет и оказался практически нефункциональным. Кроме того,
для своего времени система MULTICS была чересчур амбициозной, подобно
аналитической машине Бэббиджа в XIX веке.
В итоге система MULTICS стала источником многих конструктивных идей для
компьютерных теоретиков, но превратить ее в серьезный продукт и добиться
коммерческого успеха оказалось намного труднее, чем ожидалось. Группа иссле
довательских лабораторий Bell Labs выбыла из проекта, а компания General
Electric совсем оставила компьютерный бизнес. Однако Массачусетсский техно
логический институт проявил упорство и со временем получил вполне работо
способную систему. В конце концов, она была продана как коммерческое из
делие компанией Honeywell, купившей компьютерный бизнес General Electric,
и установлена примерно в восьмидесяти больших компаниях и университетах по
всему миру. Несмотря на небольшой тираж системы MULTICS, ее пользователи
проявили исключительную лояльность к своему приобретению. Компании General
Motors, Ford и Национальное агентство безопасности США свернули системы
MULTICS лишь в конце 90-х, а последняя машина MULTICS, работавшая в Ми
нистерстве обороны Канады, была снята с эксплуатации в октябре 2000 г. Не
смотря на неудачу с точки зрения коммерции, система MULTICS значительно
повлияла на последующие операционные системы [24, 25, 28, 94, 101]. В Интерне
те имеется веб-сайт, на котором представлена обширная информация о системе
MULTICS, ее разработчиках и пользователях (www.multicians.org).
Словосочетание «компьютерное приложение» вышло из употребления, однако
в последние годы его идея получила «вторую жизнь». В простейшем случае компь
ютеры, или рабочие станции (персональные компьютеры большой мощности),
расположенные в компании или классной комнате, посредством локальной сети
подключаются к файловому серверу, хранящему все программы и данные. При
такой топологии системный администратор устанавливает и обеспечивает защиту
единственного набора программ и данных. Администратору не нужно заботить
ся об извлечении и сохранении локальных данных неисправного компьютера;
он может без проблем переустановить его программное обеспечение. В неодно
родном окружении появляется дополнительный класс программ, называемых
32 Глава 1. Введение
1.3.1. Процессы
Ключевое понятие MINIX 3 и любой другой операционной системы — процесс.
Процессом, по существу, называют программу в момент ее выполнения. С каж
дым процессом связывается его адресное пространство — список адресов в па
мяти от некоторого минимума (обычно нуля) до некоторого максимума, которые
процесс может прочесть и в которые он может писать. Адресное пространство
содержит саму программу, данные к ней и ее стек. Со всяким процессом связы
вается некий набор регистров, включая счетчик команд, указатель стека и другие
аппаратные регистры, плюс вся остальная информация, необходимая для запус
ка программы.
Мы более детально рассмотрим понятие процесса в главе 2, но сейчас для того,
чтобы интуитивно осознать, что это такое, вспомним о многозадачных системах.
Предположим, что операционная система периодически решает остановить ра
боту одного процесса и запустить другой, потому что первый израсходовал отве
денную для него часть рабочего времени центрального процессора в прошедшую
секунду.
Если процесс был приостановлен подобным образом, позже он должен быть за
пущен заново из того же состояния, в каком его остановили. Следовательно, всю
относящуюся к процессу информацию нужно где-либо явно хранить на время
его приостановки. Например, процесс может одновременно открыть для чтения
несколько файлов. Связанный с каждым файлом указатель дает текущую пози
цию (то есть номер байта или записи, которые будут прочитаны следующими).
При приостановке процесса все указатели нужно сохранить так, чтобы команда
чтения, выполненная после возобновления выполнения процесса, прочла пра
вильные данные. Во многих операционных системах вся информация каждого
процесса, дополняющая содержимое его собственного адресного пространства,
хранится в таблице операционной системы. Эта таблица называется таблицей
процессов и представляет собой массив (или связанный список) структур, по од
ной на каждый существующий в данный момент процесс.
Таким образом, приостановленный процесс состоит из собственного адресного
пространства, обычно называемого образом памяти (core image1), и компонентов
таблицы процесса, содержащей, помимо других величин, значения его регистров.
Главными системными вызовами, управляющими процессами, являются вызовы,
связанные с созданием и завершением процессов. Рассмотрим типичный при
мер. Процесс, называемый интерпретатором команд, или оболочкой (shell), чи
тает команды с терминала. Пользователь только что напечатал команду, содер
жащую запрос на компиляцию программы. После этого оболочка должна создать
новый процесс, который запустит компилятор. Когда процесс закончит компи
ляцию, он выполнит системный вызов, завершающий его собственную работу.1
1 Слово «соге», которое можно перевести как «сердечник», напоминает об использовавшейся дав
ным-давно памяти на магнитных сердечниках.
1.3. Основные концепции 41
1.3.2. Файлы
Другая обширная группа системных вызовов относится к файловой системе. Как
было замечено ранее, основной функцией операционной системы является скры
тие особенностей устройства дисков и других устройств ввода-вывода и предо
ставление пользователю понятной и удобной абстрактной модели независимых
от устройств файлов. Системные вызовы очевидно необходимы для создания,
удаления, чтения или записи файлов. Перед тем как прочитать файл, его нужно
разместить на диске и открыть, а после прочтения его нужно закрыть. Все эти
функции осуществляют системные вызовы.
Предоставляя место для хранения файлов, операционные системы используют
понятие каталога (directory) как средства объединения файлов в группы. На
пример, студент может иметь по одному каталогу для каждого изучаемого им
курса (для программ, необходимых в рамках этого курса), каталог для элек
тронной почты и еще один — для своей домашней веб-страницы. Для создания
и удаления каталогов также необходимы системные вызовы. Они же обеспечива
ют перемещение существующего файла в каталог и удаление файла из каталога.
Содержимое каталогов могут составлять файлы или другие каталоги. Эта мо
дель образует иерархию — файловую систему (рис. 1.6).
Иерархии и процессов, и файлов организованы в виде деревьев, однако на этом
их сходство заканчивается. Иерархия процессов обычно не очень глубока (в ней
редко бывает больше трех уровней), тогда как файловая структура достаточно
часто имеет четыре, пять или даже больше уровней в глубину. Иерархия процес
1.3. Основные концепции 43
сов обычно существует очень недолго, как правило, несколько минут, иерархия
каталогов может существовать годами. Механизмы принадлежности и защиты
также различны для процессов и файлов. Обычно только родительский процесс
может управлять дочерним процессом или даже просто иметь к нему доступ, в то
же время практически всегда существует механизм, позволяющий читать файлы
и каталоги не только владельцу файла, но и более широкой группе пользователей.
Корневой каталог
Каждый файл в иерархии каталогов можно определить, задав его имя пути, на
зываемое обычно полным именем файла. Путь начинается из вершины структуры
каталогов, называемой корневым каталогом. Такое абсолютное имя пути состо
ит из списка каталогов, которые нужно пройти от корневого каталога к файлу,
с разделением отдельных компонентов косой чертой. На рис. 1.6 путь к файлу
CS101 выглядит как /Faculty/Prof.Brown/Courses/CSIOl. Первая косая
черта говорит о том, что этот путь — абсолютный, то есть начинается от корнево
го каталога. В MS-DOSh Windows для разделения компонентов вместо символа
косой черты используется символ обратной косой черты (\). Тогда этот путь
будет выглядеть так: \Faculty\Prof . Brown\Courses\CS101. В нашей книге
для записи пути мы в основном будем использовать соглашения UNIX.
В каждый момент времени у каждого процесса есть текущий рабочий каталог, в ко
тором ищутся имена путей, не начинающиеся с косой черты. Например, если на
рис. 1.6 каталог /Faculty/Prof. Brown является рабочим, то использование
44 Глава 1. Введение
пути Courses/CS101 даст тот же самый файл, что и показанный ранее абсолют
ный путь. Процессы могут изменять свой рабочий каталог, используя системные
вызовы.
В операционной системе MINIX 3 каждому файлу и каталогу присваивается
И-разрядный двоичный код защиты. Код защиты включает три 3-разрядных поля:
одно — для владельца, одно — для прочих членов группы владельца (разбиение
пользователей на группы осуществляется системным администратором) и од
но — для всех остальных пользователей. Два оставшихся бита мы рассмотрим
позднее. В каждом поле имеется бит доступа по чтению, бит доступа по записи и
бит доступа по исполнению. Эти три бита в совокупности называют rwx-бита-
ми1. К примеру, код защиты rwxr-x--x означает, что владелец файла может
читать, записывать и запускать файл, другие члены группы владельца — только
читать и запускать файл (без права записи), и, наконец, все прочие пользовате
ли — только исполнять файл (без права чтения и записи). Для каталога, в от
личие от файла, доступ по исполнению означает возможность поиска. Прочерк
указывает на отсутствие разрешения (значение соответствующего бита равно 0).
Перед тем как прочесть или записать файл, его нужно открыть, в это же время
проверяется разрешение доступа. Если доступ разрешен, система возвращает не
большое целое число, называемое дескриптором файла и используемое в после
дующих операциях. Если доступ запрещен, то возвращается код ошибки (-1).
Другое важное понятие в MINIX 3 — это смонтированная файловая система.
Почти все персональные компьютеры имеют один или несколько дисководов
для компакт-дисков, куда можно вставить и откуда можно вынуть диск. Что
бы предоставить возможность общения со сменными носителями (CD-дисками,
DVD-дисками, дискетами, Zip-дисками), MINIX 3 позволяет присоединять фай
ловую систему сменного диска к главному дереву. Рассмотрим ситуацию, пока
занную на рис. 1.7, а. Перед вызовом системной процедуры mount корневая фай
ловая система на жестком диске и вторая файловая система на компакт-диске
существуют раздельно и никак не связаны между собой.
Однако файлы на компакт-диске нельзя использовать, потому что для них не
возможно определить путь. MINIX 3 не позволяет присоединять к началу пути
название диска или его номер, так как это привело бы к жесткой зависимости
от устройств, которой операционная система должна избегать. Вместо этого
системный вызов mount позволяет присоединять файловую систему на гибком
диске к корневой файловой системе в том месте, где этого захочет программа.
На рис. 1.7, б файловая система диска 0 установлена в каталог Ь, таким обра
зом, обеспечен доступ к файлам по путям /b/х/ и /b/у. Если каталог Ь со
держал какие-либо файлы, они будут недоступны, пока смонтирован гибкий
диск, так как теперь имя /Ь ссылается на корневой каталог диска 0. (Невоз
можность доступа к этим файлам не так страшна, как кажется с первого взгля
да: файловые системы почти всегда устанавливаются в пустые каталоги.) Если
1 Аббревиатура rwx образована тремя английскими словами: read — чтение, write — запись, execution —
исполнение — Примеч. пер.
1.3. Основные концепции 45
система содержит несколько жестких дисков, они все могут быть встроены в од
но дерево таким же образом.
а б
Рис. 1.7. Монтирование файловой системы: а — перед монтированием файлы на диске О
недоступны; б — после монтирования они становятся частью общей файловой структуры
Процесс Процесс
1.3.3. Оболочка
Операционная система представляет собой программу, выполняющую систем
ные вызовы. Редакторы, компиляторы, ассемблеры, компоновщики и командные
интерпретаторы не являются частью операционной системы, несмотря на их
большую важность и полезность. Поскольку есть риск запутаться в этих поняти
ях, мы кратко рассмотрим только командный интерпретатор MINIX 3, называе
мый оболочкой (shell). Хотя оболочка не входит в операционную систему, но во
всю пользуется многими функциями операционной системы и поэтому является
хорошим примером того, как могут применяться системные вызовы. Кроме это
го, оболочка предоставляет основной интерфейс между пользователем, сидящим
за своим терминалом, и операционной системой, если, конечно, пользователь не
использует графический интерфейс. Существуют целый ряд оболочек, включая
csh, ksh, zsh и bash. Все они поддерживают описываемую здесь функциональ
ность исходной оболочки (sh).
Когда какой-либо пользователь входит в систему, запускается оболочка. Стан
дартным входным и выходным устройством для оболочки является терминал.
Оболочка начинает работу с печати приглашения (prompt) — знака доллара,
говорящего пользователю, что оболочка ожидает ввода команды. После этого
пользователь может вводить команды, например:
date
Вызов Описание
n = read(fd, buffer, nbytes) Читает данные из файла в буфер
n = write(fd, buffer, nbytes) Пишет данные из буфера в файл
pos = lseek(fd, offset, whence) Передвигает указатель файла
s = stat(name, &buf) Получает информацию о состоянии файла
s = fstat(fd, &buf) Получает информацию о состоянии файла
fd = dup(fd) Закрепляет за открытым файлом новый дескриптор
s = pipe(&fd[O]) Создает канал
s = ioctl(fd, request, argp) Специальные действия с файлом
s = access(name, amode) Проверяет доступность файла
s = rename(old, new) Переименовывает файл
s = fcntl(fd, cmd, ...) Захватывает файл и выполняет другие действия
Управление каталогами и файловой системой
s = mkdir(name, mode) Создает новый каталог
s = rmdir(name) Удаляет пустой каталог
s = Iink(name1, name2) Создает новый элемент с именем name2, указывающий
на пате1
s = unlink(name) Удаляет элемент каталога
s = mount(special, name, flag) Монтирует файловую систему
s = umount(special) Демонтирует файловую систему
s = sync() Сбрасывает все кэшированные блоки на диск
s = chdir(dirname) Изменяет рабочий каталог
s = chroot(dirname) Изменяет корневой каталог
Защита
s = chmod(name, mode) Изменяет биты защиты файла
uid = getuid() Определяет идентификатор пользователя для вызвавшего
gid = getgid() Определяет идентификатор группы для вызвавшего
s = setuid(uid) Устанавливает идентификатор пользователя для вызвавшего
s = setgid(gid) Устанавливает идентификатор группы для вызвавшего
s = chown(name, owner, group) Меняет идентификатор владельца файла
oldmask = umask(complmode) Меняет режим маскирования
Управление временем
seconds = time(&seconds) Получает время, прошедшее с 1 января 1970 года
s = stime(tp) Устанавливает время, прошедшее с 1 января 1970 года
s = utime(file, timep) Устанавливает время последнего доступа к файлу
s = times( buffer) Определяет время работы пользовательского процесса
и системы
Эта команда используется для копирования файла filel в файл f ile2. После
создания оболочкой дочернего процесса последний находит и исполняет файл
ср и передает ему имена исходного и целевого файлов.
Основной модуль программы ср (как и большинство других головных программ
на языке С) содержит определение:
main(argc, argv, envp)
использовать его при завершении работы. У него есть всего один параметр, статус
выхода, изменяющийся от 0 до 255. Он возвращается родительскому процессу
через переменную statloc в системном вызове waitpid. Младший байт этой
переменной содержит значение статуса выхода, который равен 0 при нормальном
завершении работы и ненулевому значению при завершении по ошибке. Старший
байт содержит статус завершения дочернего процесса (от 0 до 255). Например:
n = waitpid(-l, &status, options);
Если родительский процесс выполнит эту команду, то его работа будет приоста
новлена до завершения дочернего процесса. Если дочерний процесс завершится,
скажем, через вызов exit с параметром 4, то когда родительский процесс про
должит работу, п будет содержать PID дочернего процесса, a statloc — значе
ние 0x0400 (в языке С принято писать символы Ох перед шестнадцатеричными
числами, это соглашение постоянно используется в книге).
В MINIX 3 под процессы отводится часть памяти, которая, в свою очередь, де
лится на три сегмента: текста (код программы), данных (переменные) и стека.
Сегмент данных растет снизу вверх, а стек увеличивается сверху вниз, как пока
зано на рис. 1.9. Между ними существует часть неиспользованного адресного
пространства. Стек автоматически занимает такую часть этого участка памяти,
какую необходимо, но расширение сегмента данных выполняется явным обра
зом. Для этого используется специальный системный вызов brk, задающий новый
адрес для границы сегмента данных. Этот адрес может быть как больше текуще
го значения (сегмент растет), так и меньше (сегмент уменьшается). Но, конечно
же, этот адрес должен быть меньше, чем указатель стека, так как в противном
случае данные и стек могут перекрываться, что недопустимо.
Адрес (шестнадцатеричный)
Рис. 1.9. Под процессы отводится три сегмента: текст, данные и стек. В данном примере
все три расположены в едином адресном пространстве, однако также поддерживаются
раздельные пространства сегментов команд и данных
В этом случае сигнал от нажатия клавиш Ctrl+C не должен влиять на работу фо
нового процесса, поэтому после вызова fore, но перед вызовом ехес оболочка
делает следующие вызовы:
sigaction(SIGINT, SIG.IGN, NULL);
sigaction(SIGQUIT, SIG_IGN, NULL);
только один сигнал. Если сделать вызов alarm, указав задержку 10 секунд, а за
тем, по истечении 3 секунд, еще раз выполнить вызов alarm с параметром 20 се
кунд, придет сигнал только от того вызова, который сделан последним. Первый
вызов отменяется. Чтобы отменить сделанный ранее вызов alarm, нужно еще
раз сделать вызов alarm, передав в качестве аргумента 0. Если пришедший сиг
нал SIGALARM не обрабатывать, то выполняется действие по умолчанию, и про
цесс завершается.
Иногда возникают ситуации, когда процессу нечем заняться до прибытия сигнала.
Например, рассмотрим компьютерную программу для проверки скорости чтения
и внимательности. Эта программа выводит некоторый текст, а затем делает вы
зов alarm с параметром 30 секунд. Пока ученик читает текст, программа ничего
не должна делать. Программа может выполнять пустой цикл, но это будет бес
смысленной тратой процессорного времени, которое может потребоваться дру
гому процессу. Гораздо лучше использовать системный вызов pause, который
заставляет MINIX 3 приостановить выполнение процесса до прихода сигнала.
Эта команда создает файл с именем /dev/ttyc2 (обычно это имя соответствует
второй консоли) и задает для него права доступа 020744 (специальный символьный
56 Глава 1. Введение
Когда пользователь дает оболочке эту команду, то оболочка создает канал и со
единяет стандартный вывод первого процесса с входом канала, а стандартный
ввод второго процесса с выходом канала. Чтобы создать канал, применяется сис
темный вызов pipe, возвращающий два дескриптора файлов: один для чтения
из канала, другой для записи в него:
58 Глава 1. Введение
pipe(&fd[0]);
Здесь f d — массив двух целых чисел, f d [ О ] — дескриптор для чтения, a f d [ 1 ] —
дескриптор для записи. Как правило, после этого делается вызов fork, роди
тельский процесс закрывает дескриптор для чтения, а дочерний процесс — де
скриптор для записи (или наоборот), чтобы один процесс мог писать в канал,
а другой — читать из него.
В листинге 1.3 приведен каркас процедуры, создающей два процесса таким обра
зом, что выход первого из них передается через канал во второй (более реали
стичный пример обеспечивал бы проверку ошибок и обработку аргументов).
Сначала создается поток, затем делается вызов fork, и родительский процесс
становится первым процессом в канале, а дочерний процесс — вторым. Так как
запускаемые файлы, processl и process2, ничего не знают о том, что они
соединяются каналом, для работы программы необходимо, чтобы стандартный
вывод первого процесса был соединен со стандартным вводом второго процесса
каналом. Сначала родительский процесс закрывает дескриптор для чтения из ка
нала. Затем он закрывает стандартный вывод и делает вызов dup, после которого
стандартным выводом становится вход канала. Важно понимать, что вызов dup
всегда возвращает наименьший допустимый дескриптор, в данном случае — 1.
Наконец, исходный дескриптор для записи в канал закрывается.
Листинг 1.3. Каркас процедуры, создающей конвейер из двух процессов
#define STD_INPUT О /* Дескриптор файла для стандартного ввода */
#define STD_OUTPUT 1 /* Дескриптор файла для стандартного вывода*/
Тогда файл memo из каталога j im появится в каталоге ast под названием note.
Соответственно, имена /usr/jim/memo и /usr/ast/note после этого будут
ссылаться на один и тот же файл.
1.4. Системные вызовы 61
а б
Рис. 1.10. Механизм выполнения вызова link: а — два каталога до присоединения
/usr/jim/memo к каталогу ast; б — те же каталоги после вызова link
Возможно, станет понятнее, что делает системный вызов link, если разобраться
в том, как он работает. Каждый файл в UNIX имеет уникальный номер, который
идентифицирует файл. Этот номер представляет собой индекс в таблице индекс
ных узлов (index nodes), или г-узлов (i-nodes), содержащей по одному индексному
узлу на файл. Каждый индексный узел включает в себя информацию о владель
це файла, о том, какие блоки на диске он занимает и т. д. Каталог представляет
собой просто файл, содержащий набор пар из номера индексного узла и ASCII-
имени. В первых версиях UNIX каждый элемент каталога занимал 16 байт: 2 на
номер индексного узла и 14 на имя. Для поддержки длинных имен файлов
требуется более сложная структура, однако концептуально каталог представля
ет собой файл с парами номеров индексных узлов и ASCII-имен. На рис. 1.10
файл mail имеет номер индексного узла 16 и т. д. Действие вызова link заклю
чается в создании нового элемента каталога, имя которого, возможно, является
новым, а номер индексного узла равен номеру индексного узла существующе
го файла. На рис. 1.10, б два элемента имеют одинаковый номер индексного
узла (70) и, таким образом, ссылаются на один и тот же файл. Если впоследст
вии один из них будет удален с помощью системного вызова unlink, другой
элемент останется. Если будут удалены оба файла, UNIX обнаружит, что больше
нет записей, соответствующих этому файлу (поле в таблице индексных узлов
хранит данные с номером элемента каталога, указывающего на файл), и удалит
файл с диска.
Как упоминалось ранее, системный вызов mount позволяет две файловых систе
мы объединить в одну. Обычная ситуация такова: на жестком диске находится
корневая файловая система, содержащая двоичные (исполняемые) версии об
щих команд и наиболее часто использующиеся файлы. При этом пользователь
может вставить в дисковод компакт-диск с файлами для чтения.
При помощи системного вызова mount файловую систему с гибкого диска можно
присоединить к корневой файловой системе, как показано на рис. 1.11. Типич
ная команда на языке С, выполняющая монтирование, выглядит так:
mount("/dev/cdromO", "/mnt", 0) ;
После вызова mount доступ к файлу на диске 0 можно получить, просто указав
его путь из корневого или рабочего каталога, независимо от того, на каком диске
он находится. В действительности второй, третий и четвертый диски тоже мож
но встроить в любое удобное место в дереве. Вызов mount позволяет объединить
съемные носители в единую интегрированную файловую структуру, не заботясь
о том, на каком из устройств фактически находится файл. Хотя в нашем примере
рассматривались компакт-диски, жесткие диски или их части, часто называемые
разделами (partition), или второстепенными устройствами (minor devices), мон
тируются аналогично. Когда файловая система более не нужна, ее можно демон
тировать с помощью системного вызова umount.
MINIX 3 поддерживает блочное кэширование, то есть система кэширует в памяти
последние блоки, к которым были совершены обращения, чтобы избежать слиш
ком частых обращений к диску. Если блок в кэше модифицирован (например,
вызовом write) и система рухнет до того, как обновленный блок будет записан
на диск, и файловая подсистема окажется поврежденной. Чтобы снизить риск
повреждения, важно периодически сбрасывать содержимое кэша на диск, чтобы
в случае сбоя системы терялось только небольшое количество данных. Систем
ный вызов sync заставляет MINIX 3 сбросить на диск все кэшированные блоки,
измененные с момента считывания. Обычно при старте MINIX вместе с систе
мой запускается фоновая программа update, каждые 30 секунд выполняющая
вызов sync.
Для работы с каталогами служат еще два вызова, chdir и chroot. Первый из
них меняет текущий каталог, а второй корневой. Например:
chdir("/usr/ast/test");
После этого вызова открытие файла xyz приведет к открытию файла /usг/
ast/test/xyz. Вызов chroot работает аналогично. Как только процесс изме
няет корневой каталог системы, все абсолютные пути (те, которые начинают
ся с символа /), начинают указывать на другой корень. Вы спросите, для чего
это нужно? С целью безопасности: серверные программы протоколов FTP (File
Transfer Protocol — протокол передачи файлов) и HTTP (HyperText Transfer
Protocol — протокол передачи гипертекста) меняют корневой каталог, чтобы
сделать недоступными все файлы, находящиеся в иерархии выше. Правом за
пуска вызова chroot обладают только суперпользователи, но и они выполня
ют его нечасто.
1.4. Системные вызовы 63
маску, применяемую для маскирования битов прав доступа к файлу при его со
здании. Например:
umask(022) ;
Если сделать такой вызов, то при вызовах с г eat или mknod в правах доступа
к создаваемому объекту будут маскироваться биты 022. Иначе говоря, следую
щий вызов создаст файл с правами доступа 0755, а не 0777:
creat("file", 0777);
Наконец, вызов times позволяет узнать, сколько времени процессор провел, ис
полняя процесс, и сколько времени потрачено на систему (то есть на обработку
системных вызовов). Кроме того, суммируется и возвращается общее время сис
темы и процесса для всех дочерних процессов.
Адрес
OxFFFFFFFF
Библиотечная
процедура
read
Пространство
пользователя
П ол ьзо вател ьская
■ программа,
вызывающая read
Пространство ядра
(операционная система)
Системные вызовы
Прерывания
1.5.4. Экзоядра
В системе VM/370 каждый процесс пользователя получает точную копию настоя
щей машины. На Pentium, в режиме виртуальной машины 8086, каждый пользо
вательский процесс получает точную копию другой машины. Шагнув несколько
дальше, исследователи из Массачусетсского технологического института изобре
ли систему, которая обеспечивает каждого пользователя абсолютной копией ре
ального компьютера, но со своим подмножеством ресурсов [43, 75]. Например,
одна виртуальная машина может получить блоки на диске с номерами от 0 до
1023, следующая — от 1024 до 2047 и т. д.
На нижнем уровне в режиме ядра работает программа, которая называется экзо-
ядро (exokernel). В ее задачу входит распределение ресурсов для виртуальных
машин, а после этого проверка их использования (то есть отслеживание попыток
машин задействовать чужой ресурс). Каждая виртуальная машина на уровне
пользователя может работать с собственной операционной системой, как на
VM/370 или виртуальных машинах 8086 для Pentium, с той разницей, что каж
дая машина ограничена набором ресурсов, которые она запросила и которые ей
были предоставлены.
Преимущество схемы экзоядра заключается в том, что она позволяет обойтись
без уровня отображения. При других методах работы каждая виртуальная маши
на считает, что она использует собственный диск с нумерацией блоков от 0 до
некоторого максимума. Поэтому монитор виртуальной машины должен поддер
живать таблицы преобразования адресов на диске (и всех других ресурсов). Не
обходимость преобразования отпадает при наличии экзоядра, которому нужно
только хранить запись о том, какой виртуальной машине выделен данный ре
сурс. Подобный подход имеет еще одно преимущество: он отделяет многозадач
ность (в экзоядре) от операционной системы пользователя (в пользовательском
пространстве) с меньшими затратами, так как для этого ему необходимо всего
лишь не допускать вмешательства одной виртуальной машины в работу другой.
нескольких виртуальных машин 370 само по себе не так просто (особенно если
вы хотите сделать это достаточно эффективно).
В развитии современных операционных систем наблюдается тенденция в сторо
ну дальнейшего переноса кода на верхние уровни и удалении при этом всего, что
только возможно, из операционной системы, оставляя минимальное ядро. Обыч
но для этого решение большинства задач операционной системы перекладывает
ся на пользовательские процессы. Получая запрос на обслуживание, например
чтение блока файла, пользовательский процесс (теперь называемый клиент
ским) посылает запрос серверному процессу, который его обрабатывает и высы
лает назад ответ.
В модели, показанной на рис. 1.15, в задачу ядра входит только управление взаи
модействием между клиентами и серверами. Благодаря разделению операци
онной системы на части, каждая из которых управляет всего одним элементом
системы (файловой подсистемой, процессами, терминалом или памятью), все
части становятся небольшими и легко управляемыми. К тому же, поскольку все
серверы работают как процессы в пользовательском режиме, а не в режиме ядра,
они не имеют прямого доступа к оборудованию. Поэтому если происходит ошиб
ка на файловом сервере, может оказаться неработоспособной служба обработки
файловых запросов, но это обычно не приводит к остановке машины в целом.
Резюме
Операционную систему можно рассматривать с двух точек зрения: как менеджер
ресурсов и как расширенную машину. Как менеджер ресурсов операционная
система рационально управляет различными частями системы. С точки зрения
расширенной машины, работа операционной системы состоит в предоставлении
пользователям виртуальной машины, более удобной, чем настоящий компьютер.
Операционные системы имеют достаточно долгую историю развития, которая на
чинается с тех дней, когда операционные системы заменили оператора, и про
должается до современных многозадачных систем.
Сердцем любой операционной системы является набор системных вызовов, ко
торые она может обработать. Они говорят о том, что реально делает операцион
ная система. Мы рассмотрели шесть групп системных вызовов для MINIX 3.
Первая группа обеспечивает создание и завершение процессов. Вторая группа
предназначена для работы с сигналами. Третья — для чтения и записи файлов.
Четвертая нужна для управления каталогами. Пятая включила в себя управле
ние защитой, а шестая — управление временем.
Операционные системы могут быть структурированы по-разному. В наиболее
общем случае структурирование может быть таким: монолитные системы, иерар
хические многоуровневые системы, виртуальные машины, экзоядра, модель кли
ент-сервер.
Вопросы и задания
1. Каковы две главные функции операционной системы?
2. В чем различие между режимом ядра и пользовательским режимом? Почему
это различие представляет важность для операционной системы?
3. Что такое многозадачность?
4. Что такое подкачка? Как вы считаете, будут ли передовые персональные ком
пьютеры будущего поддерживать подкачку данных в качестве стандартной
функции?
5. На ранних компьютерах чтение или запись каждого байта данных управля
лись напрямую центральным процессором (то есть тогда не было прямого
доступа к памяти). Какой смысл имеет это понятие для многозадачности?
6. Почему системы разделения времени не были широко распространены на
компьютерах второго поколения?
7. Какая из следующих команд должна быть разрешена только в режиме ядра:
1) отключение всех прерываний;
2) чтение счетчика даты/времени;
3) изменения счетчика даты/времени;
4) изменение схемы распределения памяти.
76 Глава 1. Введение
После ввода имени getty запускает системным вызовом ехес процесс login,
передав ему имя в качестве аргумента. Если вход пользователя в систему оказал
ся удачным, login также при помощи вызова ехес запускает оболочку поль
зователя. Таким образом, оболочка является дочерним процессом по отношению
к init. Команды пользователя порождают дочерние процессы для оболочки,
являющиеся «внуками» init. Описанная последовательность событий демон
стрирует использование деревьев процессов. Коды сервера реинкарнации, про
цесса init и оболочки не представлены в данной книге, поскольку продолжать
обсуждение до бесконечности невозможно. Тем не менее теперь у вас есть базо
вое представление о концепции.
( Действие )
1 • Процесс блокируется, ожидая входных данных
1 / 3~W ^\2 2. Планировщик выбирает другой процесс
_ ЕС, - 3. Планировщик выбирает этот процесс
(^кироыш) 4 >7ътоВнос1) 4- Дост*пны вх°Аные «анные
Рис. 2.2. Процесс может находиться в состоянии выполнения, готовности или блокировки.
Стрелками показаны возможные переходы между состояниями
С позиций логики первые два состояния схожи. В обоих вариантах процесс мо
жет быть запущен, только во втором случае процессор недоступен. Третье со
стояние отличается тем, что запустить процесс невозможно, какой бы ни была
загруженность процессора.
Как показано на рисунке, между этими тремя состояниями допустимы четыре
перехода. Переход 1 происходит, когда процесс обнаруживает, что продолжение
работы невозможно. В некоторых системах процесс должен выполнить систем
ный запрос, например block или pause, чтобы оказаться в состоянии блоки
ровки. В других системах, например в MINIX 3, процесс автоматически блоки
руется, если при считывании из канала или специального файла (предположим,
терминала) входные данные не обнаружены.
Переходы 2 и 3 вызываются частью операционной системы, называемой пла
нировщиком процессов, так что сами процессы даже не знают о существовании
этих переходов. Переход 2 происходит, когда планировщик решает, что пора
предоставить процессор следующему процессу. Переход 3 имеет место, когда
все остальные процессы уже исчерпали предоставленное им время, и процессор
снова возвращается к первому процессу. Вопрос планирования (когда следует
запустить очередной процесс и на какое время) сам по себе достаточно важен.
Было разработано множество алгоритмов, призванных сбалансировать требо
вания по эффективности системы в целом и каждого процесса в отдельно
сти. Мы рассмотрим планирование и изучим некоторые его алгоритмы позднее
в этой главе.
Переход 4 происходит с появлением внешнего события, ожидавшегося процес
сом (например, поступления входных данных). Если в этот момент не запущен
какой-либо другой процесс, происходит переход 3, и процесс запускается. В про
тивном случае процессу придется некоторое время находиться в состоянии го
товности, пока не освободится процессор.
Модель процессов упрощает представление о внутреннем поведении системы.
Некоторые процессы запускают программы, выполняющие команды, введенные
с клавиатуры пользователем. Другие процессы являются частью системы и об
служивают такие задания, как выполнение запросов файловой подсистемы,
управление запуском диска или магнитного накопителя. В случае дискового пре
рывания система может остановить текущий процесс и запустить дисковый про
цесс, который был заблокирован в ожидании этого прерывания. Слово «может»
использовано потому, что выполнение или невыполнение системой указанных
86 Глава 2. Процессы
Процессы
0 1 п-2 п -1
Планировщик
Таблица 2.1. Некоторые поля типовой записи таблицы процессов MINIX 3. Поля
сгруппированы для ядра, модулей управления процессами и файловой системой
Ядро Управление процессами Управление файлами
Регистры Указатель на текстовый сегмент Маска UMASK
Счетчик команд Указатель на сегмент данных Корневой каталог
Слово состояния Указатель на сегмент стека Рабочий каталог
программы Статус завершения Дескрипторы файлов
Указатель стека Состояние сигналов Реальный идентификатор
Состояние процесса Идентификатор процесса пользователя
Текущий приоритет Родительский процесс Эффективный
Максимальный приоритет идентификатор
Группа процесса пользователя
Оставшееся число тактов Процессорное время дочернего Реальный идентификатор
Размер кванта процесса группы
Использованное Реальный идентификатор Эффективный
процессорное время пользователя (UID) идентификатор группы
Указатели очереди Эффективный идентификатор Управление
сообщений пользователя
Область сохранения для
Биты действующих Реальный идентификатор группы (GID) чтения/записи
сигналов Эффективный идентификатор группы Параметры системного
Различные флаги Файловая информация о совместном вызова
Имя процесса использовании текста Различные битовые флаги
Битовые маски для сигналов
Различные битовые флаги
Имя процесса
Пространство
пользователя
Пространство
ядра
программный поток? Что случится, если один программный поток изменит обт
работник сигнала, не предупредив об этом остальные программные потоки?
Последняя проблема, порождаемая программными потоками, — управление сте
ками. Во многих системах при переполнении стека процесса ядро автоматически
увеличивает его. Если у процесса несколько программных потоков, стеков тоже
должно быть несколько. Если ядро не знает о существовании этих стеков, оно не
может их автоматически наращивать при переполнении. Ядро может даже не
связать ошибки памяти с переполнением стеков.
Разумеется, эти проблемы преодолимы, но на их примере хорошо видно, что
введение программных потоков в существующую систему без тщательной и про
думанной реконструкции всей системы не имеет смысла. По крайней мере, при
дется изменить семантику системных запросов и переписать библиотеки. И ре
зультат ваших трудов должен быть совместим с существующими программами
для процессов с одним программным потоком. Дополнительную информацию
о программных потоках см. в [56, 83].
2.2.1. Гонки
В некоторых операционных системах процессы, работающие совместно, сообща
используют некое общее хранилище данных. Каждый из процессов может счи
тывать что-либо из общего хранилища данных и записывать туда информацию.
Это хранилище представляет собой область в основной памяти (возможно, в струк
туре данных ядра) или файл общего доступа. Местоположение разделяемой па
мяти не влияет на суть взаимодействия и возникающие проблемы. Рассмотрим
взаимодействие между процессами на простом, но очень распространенном при
мере системы буферизации (спулере) печати. Если процессу требуется вывести
на печать файл, он помещает имя файла в специальный каталог спулера. Другой
процесс, демон печати, периодически проверяет наличие отправленных на пе
чать файлов, печатает их и удаляет их имена из каталога.
Представьте, что каталог спулера состоит из большого числа сегментов, прону
мерованных последовательно (0, 1, 2, ...), в каждом их которых может храниться
имя файла. Также есть две совместно используемые переменные: out, указываю
щая на следующий файл для печати, и in, указывающая на следующий свобод
ный сегмент. Эти две переменные можно хранить в одном файле (состоящем из
двух слов), доступном всем процессам. Пусть в данный момент сегменты с 0 по 3
пусты (соответствующие файлы уже напечатаны), а сегменты с 4 по 6 заняты
(файлы ждут своей очереди на печать). Пусть более или менее одновременно
процессы А и В решают поставить файл в очередь на печать. Описанную ситуа
цию схематически иллюстрирует рис. 2.5.
Директория
спулера
переменной next_f ree_slot. В данный момент оба процесса считают, что сле
дующий свободный сегмент — седьмой. Процесс В сохраняет в каталоге спулера
имя файла и заменяет значение in на 8, затем продолжает заниматься своими
задачами, не связанными с печатью.
Наконец, управление переходит к процессу Л, и он продолжает с того места, на
котором остановился. Он обращается к переменной next__f ree_slot, считыва
ет ее значение и записывает в сегмент 7 имя файла (разумеется, удаляя при этом
имя файла, помещенное туда процессом В). Затем он заменяет значение in на 8
(next_f ree_slot + 1=8). Структура каталога спулера не нарушена, поэтому
демон печати не заподозрит ничего плохого, но файл процесса В не будет напе
чатан. Пользователь, связанный с процессом В, может в этой ситуации полдня
описывать круги вокруг принтера, ожидая результата. Ситуации, в которых два
(и более) процесса считывают или записывают данные одновременно, а конеч
ный результат зависит от того, какой из них был первым, называются гонками.
Отладка программы, в которой вероятно возникновение условий гонок, вряд ли
может доставить удовольствие. Результаты большинства тестов будут хороши
ми, но изредка будет происходить нечто странное и необъяснимое.
Процесс В
В блокирован |
Ti Т2 Т3
Время--------- ►
Рис. 2.6. Взаимное исключение с использованием критических секций
Запрет на прерывания
Самое простое решение состоит в запрете всех прерываний при входе процесса
в критическую секцию и разрешение прерываний по выходу из нее. Если преры
вания запрещены, невозможно прерывание по таймеру. Запрет на прерывания
исключает передачу процессора другому процессу. Таким образом, запретив пре
рывания, процесс может спокойно считывать и сохранять общие данные, не опа
саясь вмешательства конкурентов.
И все же было бы неразумно давать пользовательскому процессу полномочия
для запрета прерываний. Представьте себе, что процесс отключил все прерывания
96 Глава 2. Процессы
Переменные блокировки
Теперь попробуем найти программное решение. Рассмотрим одну совместно ис
пользуемую переменную блокировки, изначально равную 0. Если процесс хочет
попасть в критическую секцию, он предварительно считывает значение пере
менной блокировки. Если переменная равна 0, процесс изменяет ее на 1 и входит
в критическую секцию. Если же переменная равна 1, то процесс ждет, пока ее
значение сменится на 0. Таким образом, 0 означает, что ни одного процесса в кри
тической секции нет, а 1 свидетельствует, что какой-либо процесс уже находится
в критической секции.
К сожалению, у предложенного метода те же проблемы, что и в примере с ката
логом буферизации печати. Представьте, что один процесс считывает перемен
ную блокировки, обнаруживает, что она равна 0, но прежде чем он успевает из
менить ее на 1, управление получает другой процесс, успешно изменяющий ее
на 1. Когда первый процесс снова получит управление, он тоже установит пере
менную блокировки в 1, и два процесса одновременно окажутся в критических
секциях.
Можно подумать, что проблема решается повторной проверкой значения пере
менной до ее замены, но это не так. Второй процесс может получить управление
как раз после того, как первый процесс закончит вторую проверку, но еще не
успеет изменить значение переменной блокировки.
Строгое чередование
Третий метод реализации взаимного исключения иллюстрирует листинг. 2.1.
Этот фрагмент программного кода, как и многие другие в данной книге, написан
на С. Язык С был выбран, поскольку практически все существующие операцион
ные системы написаны на С (или C++), а не на языках, подобных Java. Язык С
обладает всеми необходимыми свойствами для написания операционных систем,
это — мощный, эффективный и предсказуемый язык программирования. А язык
Java, например, не является предсказуемым, поскольку у программы, написан
ной на нем, может в критический момент закончиться свободная память, и она
вызовет «сборщик мусора» в исключительно неподходящее время. В случае С
это невозможно, поскольку в С процедура сборки мусора в принципе отсутствует.
2.2. Взаимодействие между процессами 97
/* Процесс 2 */
while(TRUE) {
while(turn!=1) /* ожидание */;
critical_region();
turn=0;
noncritical_region();
}
В листинге 2.1 целая переменная turn, изначально равная 0, фиксирует, чья оче
редь входить в критическую секцию. Вначале процесс 0 проверяет значение turn,
считывает 0 и входит в критическую секцию. Процесс 1 также проверяет значе
ние turn, считывает 0 и после этого в цикле непрерывно проверяет, когда же
значение turn будет равно 1. Постоянная проверка значения переменной в ожи
дании некоторого значения называется активным ожиданием. Однако подобного
подхода следует избегать, поскольку он является причиной бесцельного расхо
дования ресурсов процессора. Активное ожидание может быть приемлемым толь
ко в случае, когда есть уверенность в коротком времени ожидания.
Когда процесс 0 покидает критическую секцию, он изменяет значение turn на 1,
позволяя процессу 1 завершить цикл. Предположим, что процесс 1 быстро поки
дает свою критическую секцию, так что оба процесса находятся в обычном со
стоянии, и значение turn равно 0. После этого процесс 0 быстро выполняет весь
цикл, выходит из критической секции и устанавливает значение turn равным 1.
В итоге в этот момент значение turn оказывается равным 1, и оба процесса на
ходятся вне критической секции.
Неожиданно процесс 0 завершает работу вне критической секции и возвращается
к началу цикла. Но на большее он не способен, поскольку значение turn равно 1,
и процесс 1 находится вне критической секции. Процесс 0 «зависает» в своем
цикле while, ожидая, пока процесс 1 изменит значение turn на 0. Получается,
что метод поочередного доступа к критической секции не слишком удачен, если
один процесс существенно медленнее другого.
Эта ситуация нарушает третье из сформулированных нами условий: один про
цесс блокируется другим, не находящимся в критической секции. Возвратимся
к примеру с каталогом спулера: если заменить критическую секцию процедурой
считывания из каталога и записи в каталог, процесс 0 не сможет послать файл на
печать, поскольку процесс 1 занят чем-то другим.
98 Глава 2. Процессы
Алгоритм Петерсона
Датский математик Деккер (Т. Dekker) был первым, кто разработал программ
ное решение проблемы взаимного исключения, не требующее строгого чередова
ния. Подробное изложение алгоритма можно найти в [38].
В 1981 году Петерсон (G. L. Peterson) придумал существенно более простой
алгоритм взаимного исключения. С этого момента вариант Деккера считается
устаревшим. Алгоритм Петерсона, представленный в листинге 2.2, состоит из
двух процедур, написанных на языке С, соответствующем стандарту ANSI, что
предполагает необходимость прототипов для всех определяемых и используе
мых функций. В целях экономии места мы не будем приводить прототипы для
этого и последующих примеров.
Листинг 2.2. Решение Петерсона для взаимного исключения
#define FALSE О
#define TRUE 1
#define N 2 /* Количество процессов */
int turn; /* Чья сейчас очередь? */
int interested[N]; /* Все переменные изначально
/* равны О (FALSE) */
Команда TSL
Рассмотрим решение, поддерживаемое аппаратно. Многие компьютеры, особен
но разработанные с расчетом на несколько процессоров, имеют в составе коман
ду TSL (Test and Set Lock — проверить и заблокировать):
TSL RX,LOCK
void producer(void)
{
int item;
2.2.5. Семафоры
В 1965 году Дейкстра (Е. W. Dijkstra) показал, как использовать целую перемен
ную для подсчета сигналов запуска, сохраненных на будущее. Им был предло
жен новый тип переменных, так называемых семафоров, значение которых мо
жет быть нулем (в случае отсутствия сохраненных сигналов активизации) или
некоторым положительным числом, соответствующим количеству отложенных
сигналов.
Дейкстра предложил две операции, down и пр (обобщения примитивов sleep
и wakeup). Операция down сравнивает значение семафора с нулем. Если значе
ние семафора больше нуля, операция down уменьшает его (то есть расходует один
из сохраненных сигналов активизации) и просто возвращает управление. Если
значение семафора равно нулю, процедура down не возвращает управление про
цессу, а процесс переводится в состояние ожидания. Все операции проверки
значения семафора, его изменения и перевода процесса в состояние ожидания
выполняются как единое и неделимое атомарное действие. Тем самым гаран
тируется, что после начала операции ни один процесс не получит доступа к се
мафору до окончания или блокирования операции. Атомарность операции чрез
вычайно важна для разрешения проблемы синхронизации и предотвращения
условий гонок.
Операция up увеличивает значение семафора. Если с этим семафором связаны
один или несколько ожидающих процессов, которые не могут завершить более
раннюю операцию down, один из них выбирается системой (например, случай
ным образом) и ему разрешается завершить свою операцию down. Таким обра
зом, после операции up, примененной к семафору, связанному с несколькими
ожидающими процессами, значение семафора так и остается равным 0, но число
ожидающих процессов уменьшается на единицу. Операция увеличения значения
семафора и активизации процесса тоже неделима. Ни один процесс не может
быть блокирован во время выполнения операции up, как ни один процесс не мог
быть блокирован во время выполнения операции wakeup в предыдущей модели.
В оригинале Дейкстра употреблял вместо down и up обозначения Р и V соответ
ственно. Мы не будем в дальнейшем использовать оригинальные обозначе
ния, поскольку тем, кто не знает голландского языка, эти обозначения ничего не
скажут (да и тем, кто знает язык, говорят немного). Впервые обозначения down
и up появились в языке Алгол 68.
void producer(void)
{
int item;
void consumer(void)
{
int item;
while (TRUE) { /* Бесконечный цикл */
down(&full); /* Уменьшить число полных сегментов буфера */
down(&mutex); /* Вход в критическую секцию */
item = remove_item(); /* Удалить элемент из буфера */
up(&mutex); /* Выход из критической секции */
up(&empty); /* Увеличить счетчик пустых сегментов буфера */
consume_item(item); /* Обработка элемента */
}
}
В представленном решении используются три семафора: один для подсчета
заполненных сегментов буфера (full), другой для подсчета пустых сегментов
(empty), а третий предназначен для исключения одновременного доступа про
изводителя и потребителя (mutex) к буферу. Значение счетчика full изначаль
но равно нулю, счетчик empty равен числу сегментов в буфере, а счетчик mutex
равен 1. Семафоры, исходное значение которых установлено в 1, предназначен
ные для исключения одновременного нахождения в критической секции двух
2.2. Взаимодействие между процессами 105
2.2.6. Мьютексы
Иногда, если не нужно применять семафор как счетчик, используется его упро
щенный вариант, называемый мьютексом (mutex). Мьютексы способны лишь
обеспечивать взаимное исключение для общего ресурса или фрагмента кода.
Их реализация отличается простотой и эффективностью, что делает мьютексы
исключительно полезными в программных потоках, реализуемых исключитель
но в пространстве пользователя.
Мьютекс представляет собой переменную, способную находиться в одном из двух
состояний: блокированном и разблокированном. Для хранения такой перемен
ной достаточно одного бита, хотя на практике мьютекс имеет целочисленный тип:
нулевое значение соответствует разблокированному состоянию, а любое ненуле
вое — блокированному. Мьютексы управляются двумя процедурами. Когда про
цессу (или программному потоку) необходимо войти в критическую секцию, он
вызывает процедуру mutex_lock. Если мьютекс в этот момент разблокирован
106 Глава 2. Процессы
2.2.7. Мониторы
Взаимодействие между процессами с применением семафоров выглядит доволь
но просто, не правда ли? Но эта простота кажущаяся. Взгляните внимательнее
на порядок выполнения процедур down перед помещением элементов в буфер
или удалением их из буфера в листинге 2.5. Представьте себе, что две процедуры
down в программе производителя поменялись местами, так что значение mutex
было уменьшено раньше, чем empty. Если буфер был заполнен, производитель
блокируется, сбросив mutex в 0. Соответственно, в следующий раз, когда потре
битель обратится к буферу, он выполнит операцию down с переменной mutex,
равной 0, и тоже заблокируется. Оба процесса навсегда оказываются заблокиро
ванными. Эта неприятная ситуация называется взаимной блокировкой (deadlock),
и мы вернемся к ней в главе 3.
Описанная ситуация показывает, с какой аккуратностью нужно обращаться с се
мафорами. Одна маленькая ошибка, и все останавливается. Это напоминает про
граммирование на ассемблере, но на самом деле еще сложнее, поскольку такие
ошибки приводят к абсолютно невоспроизводимым и непредсказуемым услови
ям гонок, взаимным блокировкам и т. п.
Чтобы упростить написание программ, Бринч Хансен (Brinch Hansen) в 1973 го
ду и Хоар (Ноаге) в 1974 году предложили примитив синхронизации более вы
сокого уровня, называемый монитором. Их предложения несколько отличались
друг от друга, как мы увидим дальше. Монитор — это набор процедур, пере
менных и других структур данных, объединенных в особый модуль, или пакет.
Процессы могут вызывать процедуры монитора, но у процедур, объявленных вне
монитора, нет прямого доступа к внутренним структурам данных монитора. По
добное правило, обычное для современных объектно-ориентированных языков,
таких как Java, было нестандартным в то время, хотя объекты поддерживались
уже в языке Simula 67. В листинге 2.6 представлен монитор, написанный на во
ображаемом языке, некоем «местечковом диалекте» — «пиджин» Pascal.
Реализации взаимных исключений способствует важное свойство монитора: при
обращении к монитору в любой момент времени активным может быть только
один процесс. Мониторы являются структурным компонентом языка програм
мирования, поэтому компилятор знает, что обрабатывать вызовы процедур мони
тора следует иначе, чем вызовы остальных процедур. Обычно при вызове процеду
ры монитора первые несколько команд процедуры проверяют, нет ли в мониторе
2.2. Взаимодействие между процессами 107
procedure producer(x) ;
end;
procedure consumer(x) ;
end;
end monitor;
procedure producer;
begin
while true do
begin
item = produce_item;
Producerconsumer.insert(item)
end
end;
procedure consumer;
begin
while true do
begin
item = Producerconsumer.remove;
consume_item(item)
end
end;
Можно изменить программу так, чтобы после получения левой вилки проверя
лась доступность правой. Если правая вилка недоступна, философ кладет левую
обратно, ждет некоторое время и повторяет весь процесс. Этот подход также не
будет работать, хотя и по другой причине. Если не повезет, все пять философов
могут начать процесс одновременно, взять левую вилку, обнаружить отсутст
вие правой, положить левую обратно на стол, одновременно взять левую вилку,
и так до бесконечности. Ситуация, в которой все программы продолжают рабо
тать сколь угодно долго, но не могут добиться хоть какого-то прогресса, называ
ется зависанием (starvation).
Вы можете подумать: «Если философы будут размышлять в течение некото
рого случайно выбранного промежутка времени после неудачной попытки взять
правую вилку, вероятность того, что все процессы будут продолжать топтать
ся на месте хотя бы в течение часа, невелика». Это правильно, и для большинст
ва приложений повторение попытки спустя некоторое время снимает проблему.
Например, в локальной сети Ethernet в ситуации, когда два компьютера посы
лают пакеты одновременно, каждый должен подождать случайно заданное вре
мя и повторить попытку — на практике это решение хорошо работает. Тем не
менее в некоторых приложениях предпочтительным является другое решение,
работающее всегда и не зависящее от случайных чисел (например, в прило
жении, предназначенном для обеспечения безопасности на атомных электро
станциях).
Листинг 2.9 можно улучшить, исключив взаимные блокировки и зависания процес
сов; для этого достаточно защитить пять команд, следующих за запросом think,
бинарным семафором. Тогда философ должен будет выполнить операцию down
для переменной mutex прежде, чем тянуться к вилкам. А после возврата вилок
на место ему следует выполнить для переменной mutex операцию up. С теорети
ческой точки зрения решение вполне подходит. С позиций практики возникают
проблемы эффективности: в каждый момент времени может есть спагетти только
один философ. Но вилок пять, поэтому логичней было бы разрешить есть в каж
дый момент времени двум философам.
Решение, представленное в листинге 2.10, исключает взаимные блокировки и по
зволяет реализовать максимально возможный параллелизм для любого числа
философов. Здесь используется массив state для отслеживания душевного со
стояния каждого философа: он либо ест, либо размышляет, либо голодает (пы
таясь получить вилки). Философ может начать есть, только если ни один из его
соседей не ест. Соседи философа i определяются макросами LEFT и RIGHT (то
есть если i = 2, то LEFT = 1 и RIGHT = 3).
В программе используется массив семафоров, по одному на философа, чтобы бло
кировать голодных философов, если их вилки заняты. Обратите внимание, что
каждый процесс запускает процедуру philosopher в качестве своей основной
программы, но остальные процедуры take_forks, put_forks и test являют
ся обычными процедурами, а не отдельными процессами.
116 Глава 2. Процессы
void reader(void)
{
while (TRUE) { /* Повторять до бесконечности */
down(&mutex) ; /* Получение монопольного доступа к гс */
rc = rc+1; /* Одним читающим процессом больше */
if (rc == 1) down(&db); /* Если этот читающий процесс — */
/* первый... */
up(&mutex); /* Отказ от монопольного доступа к гс */
read_data_base(); /* Доступ к данным */
down(&mutex); /* Получение монопольного доступа к гс */
rc = rc-1; /* Одним читающим процессом меньше */
if (rc == 0) up(&db); /* Если этот читающий процесс — /*
/* последний... */
up(&mutex); /* Отказ от монопольного доступа к гс */
use_data_read(); /* Вне критической секции */
void writer(void)
{
while (TRUE) { /* Повторять до бесконечности */
think_up_data() ; /* Вне критической секции */
down(&db); /* Получение монопольного доступа */
write_data_base(); /* Запись данных */
up(&db); /* Отказ от монопольного доступа */
Первый читающий процесс выполняет операцию down для семафора db, чтобы
получить доступ к базе. Последующие читатели просто увеличивают значение
счетчика гс. По мере уменьшения числа читающих из базы значение счетчика
уменьшается, и последний читающий процесс выполняет для семафора db опера
цию up, позволяя блокированному пишущему процессу получить доступ к базе.
В этом решении один момент требует комментариев. Представьте, что в то время
как один читатель уже пользуется базой, другой читатель запрашивает к ней
доступ. Доступ разрешается, поскольку читающие процессы друг другу не меша
ют. Доступ разрешается и третьему, и последующим читателям.
118 Глава 2. Процессы
2.4. Планирование
Когда компьютер работает в многозадачном режиме, на нем могут быть активны
ми несколько процессов, пытающихся одновременно получить доступ к процес
сору. Эта ситуация возникает, когда два и более процессов находятся в состоя
нии готовности. Если доступен только один процессор, необходимо выбирать
между процессами. Отвечающая за это часть операционной системы называется
планировщиком, а используемый алгоритм — алгоритмом планирования.
Многие вопросы планирования в одинаковой степени касаются процессов и про
граммных потоков. Сначала мы акцентируем внимание на планировании про
цессов, а затем коснемся специфических вопросов планирования программных
потоков.
Поведение процесса
Почти все процессы чередуют периоды интенсивной вычислительной работы
с вводом-выводом (в том числе дисковым), как показано на рис. 2.8. Как прави
ло, в течение некоторого времени процессор работает без остановки, а затем
выполняется системный вызов на запись или чтение файла. По завершении сис
темного вызова процессор вновь продолжает вычисления до тех пор, пока не воз
никнет потребность получения или записи данных и т. д. Обратите внимание, что
некоторые действия по вводу-выводу считаются вычислительными. Примером
служит копирование процессором битов в видеопамять для обновления экрана.
Здесь мы имеем дело именно с вычислениями, поскольку в данной операции за
действован центральный процессор. Под вводом-выводом в таком контексте сле
дует понимать ситуацию, когда процесс блокируется и ожидает окончания работы
внешнего устройства.
а г—ESHSHSESSSil
Время______
Рисунок 2.8 иллюстрирует одну важную вещь. Некоторые процессы (рис. 2.8, а)
большую часть своего времени тратят на вычисления, другие же процессы
(рис. 2.8, б) в основном ожидают ввода-вывода. Первые ограничены вычисли
тельными возможностями, вторые — возможностями ввода-вывода. Процессы,
ограниченные вычислительными возможностями процессора, имеют длительные
периоды вычислений, а ожидания ввода-вывода происходят редко. Процессы,
ограниченные возможностями ввода-вывода, напротив, регулярно тратят время
на ожидание ввода-вывода, а их вычислительные циклы невелики. Обратите
внимание на то, что ключевым фактором является продолжительность вычисли
тельного цикла, а не цикла ввода-вывода. Ограниченность процессов возможно
стями ввода-вывода возникает не потому, что они выполняют мало вычислений
между запросами на ввод-вывод, а потому, что обслуживание этих запросов яв
ляется длительным. Время считывания дискового блока не зависит от скорости
обработки считанных данных.
Следует заметить, что с повышением быстродействия процессоров узким ме
стом для процессов становится ввод-вывод. Это объясняется тем, что прогресс
120 Глава 2. Процессы
♦ Интерактивные системы:
4 время отклика — быстрая реакция на запросы;
♦ пропорциональность — соответствие ожиданиям пользователей.
♦ Системы реального времени:
♦ соответствие временным ограничениям — избежание потерь данных;
♦ предсказуемость — избежание потери качества в мультимедиа-системах.
При всех обстоятельствах необходимо справедливое распределение процессор
ного времени. Сопоставимые процессы должны получать сопоставимое обслу
живание. Выделять одному процессу намного больше ресурсов процессора, чем
другому, эквивалентному, несправедливо. Разумеется, с различными категория
ми процессов следует обращаться совершенно по-разному. Сравните, например,
задачи обеспечения безопасности атомной электростанции и начисления зара
ботной платы в компьютерном центре.
Применение системных политик связано со справедливым распределением ре
сурсов. Если, к примеру, локальная политика требует, чтобы процессы управле
ния безопасностью могли быть запущены в любой момент даже при условии, что
программа выдачи платежных ведомостей будет задержана на 30 секунд, плани
ровщик должен обеспечить выполнение таких условий работы.
Еще одна общая цель планировщика — по возможности загружать все части сис
темы работой. Если процессор и все устройства ввода-вывода работают непре
рывно, их производительность выше, чем при простое каких-либо компонентов.
Например, в системе пакетной обработки планировщик решает, какие процессы
загружать в память для выполнения. Предпочтительнее иметь в памяти несколь
ко из процессов, ограниченных возможностями процессора и ввода-вывода, чем
сначала загрузить и выполнить только первые, а затем — только вторые. Процес
сы, ограниченные вычислительными возможностями, начнут конкурировать за
вычислительный ресурс, а диск в это время будет простаивать. Когда очередь
дойдет до процессов, ограниченных возможностями ввода-вывода, будет про
стаивать процессор, поскольку основная борьба развернется за устройства вво
да-вывода. Лучше всего поддерживать в работе всю систему, тщательно отбирая
процессы для обработки.
Как правило, менеджеры корпоративных компьютерных центров с интенсивной
пакетной обработкой оценивают производительность подведомственных систем
по трем показателям: пропускной способности, времени оборота и коэффициенту
использования процессора. Пропускной способностью называется число зада
ний, выполняемых системой в секунду. Ясно, что 50 заданий в секунду лучше,
чем 40. Время оборота — это среднее время с момента ввода задания до его вы
полнения. Оно показывает, сколько в среднем пользователь вынужден ждать
результатов обработки. Нетрудно сформулировать правило: чем меньше время
оборота, тем лучше.
Алгоритм планирования, максимизирующий пропускную способность, совсем не
обязательно сводит к минимуму время оборота. Например, если планировщик
выбирает из группы заданий в первую очередь самые короткие, а затем — самые
2.4. Планирование 123
8 4 4 4 4 4 4 8
А В С D В С D А
Рис. 2.9. Пример планирования по алгоритму «самое короткое задание — первое»: а — запуск
четырех заданий в исходном порядке; б — запуск в соответствии с алгоритмом
сразу же, а три оставшиеся — через три минуты. Время выполнения этих за
даний составляет 2, 4, 1, 1 и 1 мин соответственно, а время оборота — 0, 0, 3, 3
и 3 мин. Вначале можно выбрать только А или В, поскольку остальные недос
тупны. Если руководствоваться алгоритмом «самое короткое задание — первое»,
задания будут запущены на обработку в следующем порядке: А, В, С, D, Ей сред
нее время оборота составит 4,6 мин. Если же запустить их в порядке В, С, D, Е, Л,
оно будет равно 4,4 мин.
Трехуровневое планирование
С определенной точки зрения системы пакетной обработки позволяют осуще
ствлять планирование на трех разных уровнях, как показано на рис. 2.10. После
поступления в систему новые задания сначала помещаются в очередь, храни
мую на жестком диске. Далее планировщик допуска решает, какие задания при
нять в систему. Остальные ожидают во входной очереди, пока не будут выбра
ны планировщиком. Как правило, контроль допуска обеспечивает желательное
сочетание заданий, ограниченных возможностями процессора и ввода-вывода.
Возможен и другой подход — короткие задания выбираются быстрее, чем дли
тельные. Планировщик допуска может выбирать задания из очереди по собст
венному усмотрению, независимо от того, какие из них пришли раньше, а какие —
позже.
После того как задание допущено в систему, для него может быть создан про
цесс, способный претендовать на ресурсы процессора. Тем не менее возможна
ситуация, в которой процессов окажется так много, что в имеющейся памяти их
не удастся разместить. В этом случае часть процессов будет вытеснена на жест
кий диск. Какие процессы вытеснить, а какие оставить в памяти, решает второй
уровень планирования, а соответствующий планировщик называется планиров
щиком памяти.
Планирование памяти должно осуществляться регулярно, поскольку процессы,
находящиеся на диске, нуждаются в обслуживании. Однако перенос процесса
с диска в оперативную память является затратным, а потому планирование имеет
смысл выполнять не чаще одного раза в секунду. Постоянное «жонглирование»
содержимым памяти приведет к тому, что значительная часть пропускной спо
собности диска будет потеряна, замедлив ввод-вывод файлов.
Чтобы оптимизировать производительность системы в целом, планировщику па
мяти имеет смысл рассчитать желаемое число и типы процессов в памяти. Число
процессов называется степенью многозадачности. Зная, какие процессы ограни
чены вычислительными возможностями, а какие — возможностями ввода-вывода,
планировщик может нацелить усилия на поддержку в памяти нужного сочета
ния процессов. В качестве очень грубого приближения можно привести следую
щий пример: если некоторый класс процессов тратит 20 % времени на вычисле
ния, то наличие в памяти 5 таких процессов обеспечит 100-процентную загрузку
процессора.
Планировщик памяти периодически анализирует каждый процесс на жестком
диске, чтобы решить, следует ли перенести его в основную память. Решение мо
жет основываться на следующих критериях.
♦ Сколько времени прошло с последнего переноса процесса в оперативную па
мять или на жесткий диск?
♦ Сколько процессорного времени получил данный процесс за последнее время?
♦ Каков объем процесса? (Небольшие процессы вряд ли переполнят память.)
♦ Насколько важным является процесс?
На третьем уровне планирования из находящихся в оперативной памяти процес
сов выбирается тот, который будет запущен следующим. Как правило, плани
ровщика этого уровня называют планировщиком процессора и именно его чаще
всего имеют в виду, говоря о планировании. Планировщик процессора может
использовать любой подходящий алгоритм с вытеснением или без вытесне
ния. Часть алгоритмов уже нами рассмотрена; другие мы рассмотрим в следую
щем разделе.
Циклическое планирование
Одним из наиболее старых, простых, справедливых и часто используемых явля
ется алгоритм циклического, или карусельного, планирования. Каждому процес
су предоставляется некоторый интервал времени процессора, так называемый
квантом. Если к концу кванта процесс все еще работает, он прерывается, а управ
ление передается другому процессу. Разумеется, если процесс блокируется или
прекращает работу раньше, переход управления происходит в этот момент. Реа
лизация циклического планирования проста. Планировщику нужно всего лишь
согласно поддерживать список процессов в состоянии готовности (рис. 2.11, а).
Исчерпав свой лимит времени, процесс отправляется в конец списка (рис. 2.11, б).
а б
Рис. 2.11. Циклическое планирование: а — список процессов в состоянии готовности;
б — список процессов в состоянии готовности после того, как процесс В
исчерпал свой квант
Приоритетное планирование
Алгоритм карусельного планирования базируется на важном допущении о том,
что все процессы равнозначны. В случае компьютера с большим числом пользо
вателей это может быть не так. Например, в университете первыми должны об
служиваться деканы, затем профессора, секретари, уборщицы, сторожа и лишь
потом студенты. Необходимость принимать во внимание подобные внешние фак
торы приводит к приоритетному планированию. Основная идея проста: каждому
процессу присваивается свой приоритет в «табеле о рангах» и управление пере
дается готовому к работе процессу с самым высоким приоритетом.
Даже на персональном компьютере с одним пользователем могут выполняться
несколько процессов, отдельные из которых являются более важными, чем дру
гие. Демон, отвечающий за пересылку электронной почты в фоновом режиме,
имеет более низкий приоритет, чем процесс, отображающий на экране видео
фильм в реальном времени.
Чтобы предотвратить бесконечное выполнение высокоприоритетных процессов,
планировщик может уменьшать приоритет выполняемого процесса с каждым
тактом (то есть по каждому прерыванию от таймера). Если в результате приори
тет текущего процесса окажется ниже приоритета следующего процесса, про
изойдет переключение. Возможно также предоставление каждому процессу мак
симального отрезка времени работы. Как только время кончается, управление
передается следующему по значимости процессу.
Приоритеты процессам могут присваиваться статически или динамически. На во
енной базе процессу, запущенному генералом, присваивается приоритет 100, пол
ковником — 90, майором — 80, капитаном — 70, лейтенантом — 60 и т. д. А в ком
мерческом компьютерном центре выполнение заданий с высоким приоритетом
может стоить 100 долларов в час, со средним — 75, с низким — 50. Команда nice
в системе UNIX позволяет пользователю добровольно снизить приоритет своих
процессов, проявляя любезность по отношению к остальным пользователям.
Справедливости ради следует отметить, что этой командой никто никогда не
пользуется.
Система вправе динамически назначать приоритеты для достижения своих целей.
Например, некоторые процессы настолько ограничены возможностями устройств
ввода-вывода, что большую часть времени проводят в ожидании завершения
соответствующих операций. Когда бы ни потребовался процессор такому про
цессу, его следует немедленно предоставить, чтобы процесс мог начать обраба
тывать следующий запрос ввода-вывода, который будет выполняться параллель
но с вычислениями другого процесса. Если заставить процесс, ограниченный
130 Глава 2. Процессы
Готовые
Начало очередей к запуску процессы
Гарантированное планирование
Принципиально другим подходом к планированию является предоставление поль
зователям реальных обещаний и затем их исполнение. Вот обещание, которое
легко произнести и легко выполнить: если вместе с вами с процессором работа
ют п пользователей, вам будет предоставлено 1/п мощности процессора. И в сис
теме с одним пользователем и п запущенными процессорами каждому достанет
ся 1/п циклов процессора.
Чтобы сдержать это обещание, система должна отслеживать распределение про
цессора между процессами с момента создания каждого процесса. Затем система
рассчитывает количество ресурсов процессора, на которое процесс имеет право,
например, время с момента создания, деленное на п. После этого можно сосчи
тать отношение времени, предоставленного процессу, к времени, на которое он
имеет право. Полученное значение 0,5 означает, что процессу выделили только
половину положенного, а 2,0 говорит о том, что процессу досталось в два раза
больше его нормы. Далее запускается процесс, у которого это отношение наи
меньшее, пока оно не станет больше, чем у его ближайшего соседа.
Лотерейное планирование
Хотя идея обещаний пользователям и их выполнения хороша, но ее трудно реа
лизовать. Для получения предсказуемых результатов применяется другой алго
ритм, называемый лотерейным планированием [126].
В его основе лежит раздача процессам «лотерейных билетов» на доступ к раз
личным ресурсам, в том числе к процессору. Когда планировщику необходимо
принять решение, выбирается случайным образом лотерейный билет, и его обла
датель получает доступ к ресурсу. Что касается доступа к процессору, «лотерея»
может разыгрываться 50 раз в секунду, и победитель получает 20 мс времени
процессора.
2.4. Планирование 133
Справедливое планирование
До настоящего момента мы планировали процессы, игнорируя вопрос о том, кто
является их владельцами. Если, к примеру, пользователь 1 запустит 9 процессов,
а пользователь 2 — 1 процесс, то, согласно циклическому или приоритетному
планированию, пользователь 1 получит 90 % процессорного времени, а пользо
ватель 2 — 10 %.
Чтобы избежать подобного «неравенства», некоторые системы используют в пла
нировании информацию о владельцах процессов. Каждому пользователю пре
доставляется определенная доля ресурсов процессора, и процессы выбираются
на исполнение таким образом, чтобы обеспечить принятый вариант разделения.
Например, если двум пользователям обещана половина процессорного времени,
система обеспечит этот показатель независимо от запущенных ими процессов.
Пусть пользователь 1 запустил четыре процесса — А, В, С и D, а пользователь
2 — единственный процесс Е. Если в системе принято циклическое планирова
ние, то всем требованиям удовлетворит следующая последовательность выпол
нения процессов:
AEBECEDEAEBECEDE...
134 Глава 2. Процессы
на обработку этих сигналов уходит, в том же порядке, 50, 30 и 100 мс, система
является планируемой, поскольку 0,5 + 0,15 + 0,2 < 1. Даже при добавлении чет
вертого сигнала с периодом в 1 с системой все равно можно будет управлять пу
тем планирования, пока время обработки сигнала не превысит 150 мс. В этих
расчетах существенным является предположение, что время переключения меж
ду процессами пренебрежимо мало.
Алгоритмы планирования для систем реального времени бывают как статиче
скими, так и динамическими. В первом случае все решения по планированию
принимаются заранее, еще до запуска системы. Во втором случае решения выно
сятся по ходу дела. Статическое планирование применимо лишь при наличии
априори точной информации о работе, которую необходимо выполнить, и огра
ничениях, которые должны быть соблюдены. Алгоритмы динамического плани
рования не выдвигают подобных требований.
Возможные: А1, А2, АЗ, А1, А2, АЗ Возможные: А1, А2, АЗ, А1, А2, АЗ
Невозможные: А1, В1, А2, В2, АЗ, ВЗ Также возможные: А1, В1, А2, В2, АЗ, ВЗ
а б
Рис. 2.13. Планирование программных потоков: а — планирование программных
потоков уровня пользователя при времени выполнения потока 5 мс и кванте 50 мс;
б — планирование программных потоков уровня ядра в тех же условиях
Уровень
Рис. 2.14. Четыре уровня структуры MINIX 3. Только процессы нижнего уровня могут
использовать привилегированные команды (команды режима ядра)
Большая часть ядра и все задания ядра и системы написаны на языке С. Однако
в некоторых небольших фрагментах ядра используется ассемблер — в тех, кото
рые отвечают за обработку прерываний, низкоуровневые механизмы управления
переключением контекста между процессами (сохранение и восстановление ре
гистров и т. п.) и устройствами управления памятью. В общем, ассемблер приме
нялся там, где ядру было необходимо непосредственное низкоуровневое взаимо
действие с аппаратным обеспечением, недоступное для языка С. При переносе
MINIX 3 на новую архитектуру ассемблерный код должен быть соответствую
щим образом переписан.
Три уровня, находящихся над ядром, могут рассматриваться как один, поскольку
ядро, в сущности, взаимодействует с ними одинаково. Уровни ограничены коман
дами пользовательского режима, а ядро планирует запуск каждого из них. Уров
ни не имеют прямого доступа к портам ввода-вывода и, более того, ни один из
них не способен обращаться к памяти за пределами выделенного ему сегмента.
Тем не менее привилегии процессов (к примеру, возможность вызывать ядро)
могут различаться. На этом и построено разделение по уровням 2, 3 и 4. Процессы
уровня 2 имеют самые широкие привилегии, процессы уровня 3 — более ограни
ченные, и, наконец, процессы уровня 4 совсем лишены привилегий. К примеру,
драйверы устройств, находящиеся на уровне 2, могут без ограничений требо
вать, чтобы системное задание выполнило чтение или запись данных в порты
ввода-вывода. Драйвер необходим каждому типу устройств — дискам, принте
рам, терминалам, сетевым интерфейсам и др. Драйверы устройств могут выпол
нять и другие вызовы ядра, например копирование считанных данных в адрес
ное пространство другого процесса.
Третий уровень включает серверы — процессы, предоставляющие полезные услу
ги пользовательским процессам. Два сервера являются обязательными. Менеджер
процессов (Process Manager, РМ) выполняет все системные вызовы MINIX 3,
связанные с запуском и остановкой процессов (например, fork, ехес и exit),
а также сигналами (например, alarm и kill, то есть вызовами, способными из
менить состояние процесса). Кроме того, менеджер процессов ответственен за
управление памятью, в частности, с помощью системного вызова brk. Файловая
система (File System, FS) выполняет все файловые системные запросы, такие
как read, mount и chdir.
Важно понимать различие между вызовами ядра и системными POSIX-вызова
ми. Вызовы ядра представляют собой низкоуровневые функции, предоставляе
мые системным заданием драйверам и серверам для выполнения необходимых
действий. Типичный вызов ядра — чтение данных из аппаратного порта ввода-
вывода. Системные POSIX-вызовы, такие как read, fork и unlink, напротив,
являются высокоуровневыми и доступны программам, расположенным на уров
не 4. Пользовательские программы выполняют большое количество систем
ных вызовов, однако не выполняют вызовов ядра. Конечно, если разрешить себе
«жонглирование» терминами, вызов ядра можно было бы назвать системным
вызовом. Механизмы выполнения обоих типов вызовов схожи, и вызовы ядра
допустимо рассматривать как особый частный случай системных вызовов.
140 Глава 2. Процессы
Запуск MINIX 3
Каким же образом запускается операционная система? Этому вкратце посвяще
ны несколько следующих страниц. О загрузке некоторых других операционных
систем вы можете узнать в [41].
В большинстве компьютеров, оснащенных дисковыми устройствами, имеется
иерархия загрузочных дисков. Как правило, если в дисководе обнаруживается
дискета, она используется в качестве загрузочного диска. Если дискеты нет, но
в устройство CD-ROM вставлен компакт-диск, система пытается загрузиться
с него. Если же ни дискета, ни компакт-диск не вставлены, в качестве загрузоч
ного выбирается первый жесткий диск. Порядок иерархии можно задать в BIOS
сразу после подачи питания на компьютер. Иногда поддерживается возможность
загрузки с других устройств, в основном сменных.
Аппаратное обеспечение считывает первый сектор первой дорожки загрузочного
диска в память и исполняет записанный на нем код. В случае дискеты этот сек
тор содержит программу начальной загрузки. Она очень мала — не более одного
сектора (512 байт). Программа начальной загрузки MINIX 3 загружает дру
гую, большую программу boot, а та, в свою очередь, — операционную систему.
В случае загрузки с жесткого диска требуется дополнительный промежуточный
этап. Жесткие диски разбиваются на разделы, и первый сектор жесткого диска
содержит небольшую программу и таблицу разделов, вместе они называются глав
ной загрузочной записью (Master Boot Record, MBR). Программная часть считы
вает таблицу разделов и выбирает активный раздел. В первом секторе активного
раздела расположена программа начальной загрузки, которая загружается, после
чего действует так же, как при загрузке с дискеты, запуская программу boot.
Устройства CD-ROM появились позже, чем дискеты и жесткие диски. При под
держке загрузки с компакт-диска существует возможность копирования в память
более чем одного сектора. Компьютер, поддерживающий загрузку с CD-ROM,
может единовременно разместить в оперативной памяти блок данных большого
объема. Как правило, в этом случае с компакт-диска загружается точная копия
загрузочной дискеты, которая используется в качестве виртуального диска. Да
лее управление передается виртуальному диску и загрузка продолжается так,
как будто в качестве физического загрузочного устройства используется диске
та. Устаревшие компьютеры, оснащенные устройством CD-ROM, но не поддер
живающие загрузку с компакт-диска, позволяют скопировать на дискету загру
зочный образ, посредством которого можно запустить систему (разумеется, при
вставленном в устройство чтения компакт-диске).
В любом случае после запуска программа boot ищет на диске различные компо
ненты системы и размещает их по нужным адресам — создает загрузочный образ.
Ключевыми составляющими загрузочного образа являются ядро (включающее
таймерное и системное задания), менеджер процессов и файловая система. В за
грузочный образ должен входить как минимум один драйвер диска. Кроме того,
в загрузочном образе имеется ряд других программ — сервер реинкарнации, вир
туальный диск, консоль, драйверы журналов и процесс init.
2.5. Процессы в MINIX 3 143
Следует особо подчеркнуть, что все части загрузочного образа являются отдель
ными программами. После загрузки необходимых компонентов — ядра, ме
неджера процессов и файловой системы — можно отдельно загрузить множество
других частей. Исключение составляет сервер реинкарнации, который должен
являться частью загрузочного образа. После инициализации он назначает обыч
ным загруженным процессам приоритеты, делающие их системными. Сервер ре
инкарнации также перезапускает драйверы в случае аварии (именно этим объ
ясняется его название). Как было отмечено ранее, загрузочный образ должен
содержать как минимум один дисковый драйвер. Если корневая файловая систе
ма подлежит копированию на виртуальный диск, также потребуется драйвер па
мяти (в противном случае его можно загрузить позднее). Драйверы tty и log
являются на загрузочном образе необязательными. Они загружаются как можно
раньше потому, что на начальной стадии запуска системы желательно иметь воз
можность отображать сообщения на консоли и записывать информацию в жур
нал. Разумеется, процесс init тоже можно загрузить позднее, однако он управ
ляет начальным конфигурированием системы, и включение его в загрузочный
образ упрощает работу.
Запуск системы — не простая операция. Программа boot вынуждена брать на
себя действия, выполняемые дисковым драйвером и файловой системой до того,
как эти компоненты будут готовы взять управление на себя. В следующем разделе
мы подробнее рассмотрим вопросы запуска MINIX 3, а сейчас достаточно сказать,
что как только процесс загрузки завершается, ядро начинает работу.
В процессе инициализации ядро запускает сначала таймерное и системное за
дания, затем — менеджер процессов и файловую систему. Менеджер процессов
и файловая система совместными усилиями запускают остальные серверы и драй
веры, входящие в загрузочный образ. После запуска и инициализации все эти
компоненты блокируются и ожидают действий. Планировщик MINIX 3 назнача
ет процессам приоритеты. Первый пользовательский процесс, init, запускается
лишь после того, как все задания, драйверы и серверы, загруженные из образа,
оказываются в состоянии блокировки. Системные компоненты, загружаемые
из загрузочного образа или в ходе инициализации, перечислены в табл. 2.3.
Таблица 2,3, Некоторые важные системные компоненты MINIX 3 (помимо них могут
присутствовать и другие, например драйвер Ethernet или сетевой сервер)
Компонент Описание Источник загрузки
kernel Ядро плюс таймерное и системное задания Загрузочный образ
pm Менеджер процессов Загрузочный образ
fs Файловая система Загрузочный образ
rs (Пере)запуск серверов и драйверов Загрузочный образ
memory Драйвер виртуального диска Загрузочный образ
log Буферизация вывода в журнал Загрузочный образ
tty Драйвер консоли и клавиатуры Загрузочный образ
driver Драйвер (bios или дискеты) Загрузочный образ
продолжение^
144 Глава 2. Процессы
будет работать слишком долго. Задача нижнего уровня MINIX 3 — скрыть пре
рывания, преобразовав их в сообщения. С точки зрения процессов, завершение
операции устройством ввода-вывода — это сообщение, передаваемое устройст
вом определенному процессу, пробуждающее его и приводящее его в состояние
готовности.
Прерывания также генерируются программно; в этом случае их иногда называют
исключениями. Описанные ранее операции send и receive транслируются сис
темной библиотекой в команды программного прерывания, эффект от которого
ничем не отличается от аппаратного прерывания. Процесс, исполняющий про
граммное прерывание, немедленно блокируется, а ядро активизируется для об
работки прерывания. Пользовательские программы не обращаются к операциям
send и receive впрямую, однако каждый раз при использовании одного из
системных вызовов, перечисленных в табл. 1.1 (непосредственно или через биб
лиотечную процедуру), происходит внутренний вызов sendrec и генерируется
программное прерывание.
Каждый раз, когда происходит прерывание процесса, независимо от источни
ка прерывания (таймер или периферийное устройство), у системы появляется
возможность принять решение, какому процессу больше требуется процессор.
Конечно, это обязательно нужно делать и при завершении процесса, но в систе
мах, подобных MINIX 3, переключения должны происходить чаще, чем заверше
ния процессов.
Планировщик MINIX использует многоуровневую систему очередей. Определе
ны шестнадцать очередей, хотя при повторной компиляции число очередей мож
но без труда увеличить или уменьшить. Самый низкий приоритет назначается
только процессу IDLE, который выполняется исключительно во время простоя.
По умолчанию пользовательские процессы запускаются с приоритетом на не
сколько уровней выше наименьшего.
Как правило, серверы в очереди имеют более высокий приоритет, чем пользова
тельские процессы, драйверы — более высокий приоритет, чем серверы, а тай
мерное и системное задания — самый высокий приоритет. Обычно в отдельно
взятый момент времени используются не все 16 возможных очередей, а лишь не
сколько. Процесс может быть перемещен из одной очереди в другую системой
или (с оговорками) пользователем при помощи команды nice. «Дополнитель
ные» уровни открывают возможности для экспериментов и по мере добавления
в систему новых драйверов позволяют изменить предлагаемые по умолчанию
параметры для оптимизации производительности. Например, если вы захотите
добавить сервер, передающий в сеть потоковые аудио- и видеоданные, вы сможе
те назначить ему более высокий стартовый приоритет, чем у других серверов.
Вы также можете снизить приоритет текущего сервера или драйвера, чтобы обес
печить более высокую производительность нового сервера.
Помимо очередей с разными приоритетами, существует еще один механизм,
обеспечивающий одним процессам преимущество над другими. Квант, то есть
временной интервал, выделяемый процессу для работы, имеет разную длитель
ность для разных процессов. Квант пользовательских процессов относительно
150 Глава 2. Процессы
короткий; драйверы и серверы же, как правило, выполняются до тех пор, пока не
входят в состояние блокировки. Квант применяется к драйверам и серверам для
того, чтобы избежать сбоев в работе системы (зависаний), однако он значи
тельно длиннее, чем у пользовательских процессов. Если процесс исчерпал свой
квант, он считается готовым к работе и помещается в конец очереди. Если же
выясняется, что исчерпавший текущий квант процесс выполнялся и в предыду
щем кванте, это трактуется как признак «зацикливания», препятствующего ра
боте процессов с более низким приоритетом. В этом случае процесс помещается
в конец более низкоприоритетной очереди. Если ситуация повторяется, приори
тет процесса снова снижается тем же способом. В конечном счете система рано
или поздно выберет другой процесс для запуска.
Процесс, приоритет которого был снижен, может снова его повысить. Если про
цесс расходует свои кванты времени, но не препятствует выполнению других про
цессов, он помещается в очередь с более высоким приоритетом и может подобным
образом достичь наивысшего доступного ему уровня. Очевидно, что такой про
цесс нуждается в процессорном времени, но не захватывает его в ущерб другим.
В рамках очереди планирование процессов осуществляется при помощи цикли
ческого алгоритма с небольшим изменением. Если процесс не исчерпал свой
квант, но вышел из состояния готовности, это воспринимается как блокирование
по вводу-выводу. Когда процесс вновь готов к выполнению, он помещается в на
чало очереди, но ему выделяется не целый квант, а лишь неиспользованная часть
кванта, в ходе которого он был заблокирован. Такой подход обеспечивает опера
тивность реакции пользовательских процессов на ввод-вывод. Процесс, исчер
павший выделенный ему квант, помещается в конец очереди согласно классиче
скому циклическому алгоритму.
Поскольку задания, как правило, имеют наивысший приоритет, за ними следуют
драйверы, затем серверы и лишь затем пользовательские процессы, последние
выполняются только в случае, если все системные процессы бездействуют. Сис
темные же процессы ради обслуживания пользовательских процессов отложены
быть не могут.
Выбирая процесс для запуска, планировщик обращается к очереди с наивысшим
приоритетом. Если один или несколько ее процессов готовы, из их числа выби
рается первый. В противном случае аналогично проверяется очередь с более
низким приоритетом и т. д. Поскольку драйверы отвечают на запросы серверов,
а серверы — на запросы пользовательских процессов, в какой-то момент все вы
сокоприоритетные процессы заканчивают свою работу и переходят в состояние
ожидания, а пользовательские процессы получают возможность выполняться и
генерируют новые запросы. Если же система обнаружит, что ни один процесс не
готов, она выберет процесс IDLE, переводящий процессор в состояние низкого
энергопотребления до появления прерывания.
По каждому сигналу таймера проверяется, не истек ли квант текущего процесса.
Если квант истек, планировщик перемещает процесс в конец очереди (для этого
не требуется никаких действий, если очередь состоит из единственного процесса).
Затем выбирается следующий процесс. Предыдущий процесс может получить
2.6. Реализация процессов в MINIX 3 151
второй квант времени подряд при двух условиях: очереди с более высокими при
оритетами отсутствуют, а в своей очереди он является единственным. В против
ном случае процессорное время получает процесс, находящийся в начале первой
по приоритету непустой очереди. Важные драйверы и серверы получают настоль
ко длинный квант, что, как правило, не прерываются таймером. Тем не менее их
приоритет может быть временно снижен в случае неполадок, чтобы предотвра
тить полную остановку системы. Возможно, сбой важного драйвера не позволит
системе выполнять полезные действия, однако, как минимум, есть шанс завер
шить ее работу корректно, без потери данных, и даже собрать отладочную ин
формацию для устранения неполадки.
ниже отметки 640 К, при этом в оставшуюся память можно загрузить несколько
пользовательских процессов.
1093 К
scr/server/pm/pm Менеджер процессов
1024 К
'// Постоянная ///
, память и память ;
;контроллера ввода--
^вывода (недоступна'
^>MINIX3)^%:
640 К
। [Монитор загрузки] ;
590 К
Память, доступная
« пользовательским ~
программам
55 К
Системное задание
scr/kernel/kernel Таймерное задание
______ Ядро______
2 К Начало области ядра
«[Используется BIOS];
1 К
[Векторы
прерываний]
0
Рис. 2.15. Структура памяти после загрузки MINIX 3 с диска. Ядро, серверы и драйверы
компилируются и компонуются независимо друг от друга. Названия соответствующих
программ перечислены слева. Размеры указаны приблизительно и не соответствуют
масштабам рисунка
Листинг 2.12. Часть основного заголовка, подключающая все заголовочные файлы, нужные
в коде. Обратите внимание на два файла const.h, один из которых находится
в каталоге include/, второй — в локальном каталоге
^include <minix/config.h> /* Этот файл ДОЛЖЕН включаться первым */
^include <ansi.h> /* Этот файл ДОЛЖЕН включаться вторым */
^include <limits.h>
#include <errno.h>
^include <sys/types.h>
#include <minix/const.h>
^include <minix/type.h>
^include <minix.syslib.h>
^include "const.h"
Сразу после строки #ifndef _ANSI_H определяется сам макрос _ANSI_H. На
значение этой конструкции состоит в том, чтобы убедиться, что заголовочный
файл будет включен только один раз. При повторном включении все его содер
жимое игнорируется. Подобная техника используется во всех файлах из катало
га include/.
Здесь нужно пояснить два момента. Во-первых, во всех последовательностях
#ifndef . . .# de fine в главных заголовочных каталогах имена файлов предва
ряются подчеркиванием. В каталогах исходных С-кодов может присутствовать
другой заголовочный файл с тем же именем; к нему будет применен тот же ме
ханизм, однако без подчеркивания. Таким образом, включение файла из глав
ного заголовочного каталога не воспрепятствует обработке одноименного за
головочного файла из локального каталога. Во-вторых, обратите внимание на
комментарий /*_ANSI_H*/ после #ifndef. Он не является обязательным, а все
го лишь помогает отслеживать вложенные конструкции #ifndef . . . #endif
и #ifdef. . . #endif. Тем не менее при написании комментариев следует со
блюдать осторожность: отсутствие комментариев лучше, чем ошибки в них.
Второй файл из include/, косвенно включаемый в большинство исходных
файлов MINIX 3, — это limits.h (строка 0100). В нем объявлены основные
размеры, относящиеся как к типам языка (разрядность целого числа), так и к опе
рационной системе (максимальная длина имени файла).
Обратите внимание на то, что номера строк в исходных текстах на сопровождаю
щем книгу компакт-диске начинаются с новой сотни с каждым новым фай
лом. Другими словами, не следует думать, что файл ansi . h содержит 100 строк,
с 00000 по 00099. По этой причине небольшие изменения, вносимые в один файл,
скорее всего (хотя и не гарантированно), не подействуют на последующие фай
лы. Кроме того, когда в листинге появляется новый файл, указывается особый
заголовок, включающий ряд символов +, имя файла и еще один ряд символов +
(без нумерации строк). Пример такого заголовка вы можете видеть между стро
ками 00068 и 00100.
160 Глава 2. Процессы
Таблица 2.4. Размеры (в битах) некоторых типов данных на 16- и 32-разрядных системах
Тип 16-разрядная система MINIX 32-разрядная система MINIX
gid_t 8 8
dev_t 16 16
pid_t 16 32
ino_t 16 32
Как было отмечено ранее, большая часть условного кода удалена из текста, однако
мы сохранили этот пример, чтобы продемонстрировать механизм использования
условных определений. Макрос _EM_WSIZE — еще один макрос проверки поддер
живаемых функций, определенный компилятором. Он задает размер слова дан
ных целевой системы в байтах. Последовательность #if. . . #else. . .#endif
создает определения «раз и навсегда», чтобы обеспечить корректную компиля
цию кода независимо от разрядности системы — 16 или 32 бит.
В операционной системе MINIX 3 широко используются несколько других файлов
из каталога include/sys/. Файл sys/sigcontext .h (строка 1600) определяет
2.6. Реализация процессов в MINIX 3 163
Такая функция может быть вызвана только кодом, расположенным в том же ис
ходном файле. Использовать макросы PRIVATE и PUBLIC необязательно, это —
только попытка исправить проблемы, вносимые соглашениями С (где по умол
чанию имена размещаются в глобальной области видимости, а должно быть
наоборот).
В оставшейся части файла const .h определяются константы, повсеместно ис
пользуемые в системе. Так, например, везде и всюду в коде фигурирует величина
базового блока памяти, зависящая от архитектуры системы. Для платформ Intel
она равна 1024 байт. В строках 2673-2681 определены другие ее значения, соот
ветствующие платформам Intel, Motorola 68000 и Sun SPARC. Кроме того, в этом
файле определяются удобные макросы МАХ и MIN. Например, для вычисления
большего из двух значений можно применить следующую запись:
z = МАХ(х, у) ;
Еще один файл, косвенно включаемый при каждой компиляции в главные заго
ловочные файлы, — это type .h (строка 2800). В нем содержится ряд описаний
ключевых типов и связанных с ними числовых констант.
168 Глава 2. Процессы
Первые две структуры определяют два различных типа карт памяти (строки
2828-2840): одна из них соответствует локальным областям (внутри пространств
ва данных процесса), а другая — удаленным областям памяти (к примеру, вирту
альному диску). Здесь самое время рассказать о концепциях обращения к памя
ти. Как мы уже упомянули, размер базового блока памяти в MINIX 3 составляет
1024 байт. Существует две ссылки на память: ссылка phys_clicks использует
ся ядром для доступа к любому элементу памяти системы, а ссылка vir_
clicks предназначена для всех процессов, отличных от ядра. Ссылка vir_
clicks всегда указывается относительно начала сегмента памяти, выделенной
определенному процессу, и ядру зачастую приходится осуществлять преобразо
вания между виртуальными (относящимися к процессам) и физическими (отно
сящимися к оперативной памяти) адресами. Подобное неудобство компенсиру
ется тем, что процесс может самостоятельно выполнять внутренние обращения
к памяти, используя ссылку vir_clicks.
Вы могли бы предположить, что для задания размера памяти обоих типов доста
точно одной ссылки. Тем не менее указание выделенной процессу памяти с по
мощью ссылки vir_clicks имеет одно преимущество: vir_clicks гарантирует,
что ни одно обращение к памяти не выйдет за границы пространства, выделенного
процессу. Такая возможность присуща современным процессорам Intel, в част
ности семейству Pentium, и называется защищенным режимом. Отсутствие за
щищенного режима в ранних процессорах 8086 и 8088 вызывало проблемы при
разработке первых версий MINIX.
Еще одна важная структура, определенная в файле type.h, — sigmsg (строки
2866-2872). При перехвате сигнала ядро должно гарантировать, что процесс,
которому был послан сигнал, при следующем своем запуске начнет выполнять
процедуру обработки сигнала, а не продолжит работу в обычном порядке. Боль
шая часть работы по управлению сигналами выполняется менеджером процес
сов. В случае перехвата сигнала менеджер процессов передает ядру структуру,
подобную sigmsg.
Структура kinf о (строки 2875-2893) используется для распространения инфор
мации о ядре среди других компонентов системы. Менеджер процессов задейст
вует эту информацию, формируя свою часть таблицы процессов.
Структуры данных и прототипы функций взаимодействия между процессами оп
ределены в заголовочном файле ipc .h (строка 3000). Наиболее важным опреде
лением в этом файле является тип message (строки 3020-3032). Его можно бы
ло бы задать как массив определенного количества байтов, но для поддержания
хорошего стиля программирования он описан как структура, содержащая объ
единение различных типов сообщений. Всего имеется семь форматов сообще
ний, с именами от mess_l до mess_8 (формат mess_6 больше не использует
ся). В структуре message есть поле m_source, представляющее собой структуру
с информацией об отправителе сообщения, поле m_type, определяющее формат
сообщения (для системного задания — SYS_EXEC), и поля с данными сообщения.
Структуры семи типов сообщений показаны на рис. 2.16. Первые две и послед
ние две структуры кажутся одинаковыми. С точки зрения размеров элементов
2.6. Реализация процессов в MINIX 3 169
данных это действительно так, однако в типах данных имеется множество разли
чий. Если на 32-разрядном процессоре Intel типы int, long и указатели на дан
ные имеют одинаковую разрядность 32 бита, это отнюдь не означает, что на другой
аппаратной платформе картина будет той же. Семь различных форматов введены
именно для упрощения перекомпиляции MINIX 3 под различные архитектуры.
Рис. 2.16. Семь типов сообщений MINIX 3. Размеры элементов сообщений зависят
от архитектуры компьютера; здесь показаны размеры для процессоров
с 32-разрядными указателями, например семейства Pentium
передачи данных типа unsigned long. Кто-то может сказать, что мы поступаем
неправильно, но если вы собираетесь перенести MINIX 3 на новую платформу,
то вам почти наверняка придется некоторое время поработать над точным фор
матом сообщений, теперь же вы предупреждены, что поведению компилятора
также нужно уделить немало внимания.
В файле ipc .h также определены прототипы ранее описанных примитивов пе
редачи сообщений (строки 3095-3101). Наряду с основными примитивами send
receive, sendrec и notify, определен и ряд других. Последние применяются
редко и являются скорее пережитками ранних этапов разработки MINIX 3. Ста
рые компьютерные программы являются источником интересных «археологиче
ских находок» и в последующих выпусках операционной системы вполне могут
«вымереть». Тем не менее без пояснений с нашей стороны некоторые читате
ли наверняка засомневаются. Неблокирующие вызовы nb_send и nb_receive
в основном заменены вызовом notify, появившимся позднее и лучше разре
шавшим проблему передачи и проверки неблокирующих сообщений. Прототип
примитива echo не имеет полей источника и приемника. Вызов echo бессмыс
лен в рабочем коде, однако полезен в процессе разработки, поскольку позволяет
определять время передачи и приема сообщений.
Один из файлов каталога include/minix, syslib.h (строка 3200) использу
ется практически повсеместно и включен в главные заголовочные файлы всех
компонентов MINIX 3, относящихся к пользовательскому пространству. Для
доступа к самому себе ядру не нужны библиотечные функции, поэтому в заголо
вочном файле ядра, src/kernel/kernel .h, ссылка на syslib.h отсутствует.
Файл syslib.h содержит прототипы библиотечных С-функций, вызываемых
из операционной системы для доступа к другим системным службам.
Мы не будем описывать детали, касающиеся библиотек С, однако многие биб
лиотечные функции являются стандартными и доступны в любом компиляторе
языка. Тем не менее функции, на которые ссылается файл syslib.h, специ
фичны для MINIX 3, и перенос MINIX 3 в систему с другим компилятором потре
бует переноса указанных функций. К счастью, это не составляет труда, посколь
ку большинство функций попросту извлекают параметры, указанные в вызове,
и вставляют их в структуру сообщения, а затем отсылают сообщение и извлека
ют результаты из ответа. Многие из таких библиотечных функций укладывают
ся в дюжину строк С-кода.
Отдельного внимания в файле syslib. h заслуживают четыре макроса досту
па к портам ввода-вывода по чтению и записи с использованием байтов и слов,
а также прототип функции sys_sdevio, на которую ссылаются все макросы
(строки 3241-3250). Важнейшим механизмом, используемым в MINIX 3 и по
зволяющим переместить все драйверы устройств в пользовательское простран
ство, является передача запросов на чтение и запись портов ввода-вывода от драй
веров устройств к ядру.
Несколько функций, которые могли бы быть включены в файл syslib.h, вынесе
ны в другой файл, sysut il. h (строка 3400), поскольку их объектный код компи
лируется в отдельную библиотеку. Две функции нуждаются в более тщательном
2.6. Реализация процессов в MINIX 3 171
числе перечисленные в листинге 2.12. Все эти файлы уже были нами рассмот
рены в двух предыдущих разделах. Затем присоединяются еще шесть заголовоч
ных файлов из локального каталога src/kernel/, имена которых заключены
в кавычки.
Файл kernel .h позволяет легко включить в исходные файлы большое количе
ство необходимых определений при помощи одной команды:
#include "kernel.h"
Иногда имеет значение то, в каком порядке включаются заголовочные файлы.
Именно использование файла kernel .h позволяет раз и навсегда гарантировать
соблюдение требуемой последовательности. Это поднимает концепцию «сделал
и забыл», реализуемую заголовочными файлами, на более высокий уровень. Ана
логичные главные заголовочные файлы имеются в каталогах с кодами других
системных компонентов, в частности файловой системы и менеджера процессов.
Теперь давайте перейдем к локальным файлам, включаемым в kernel. h. В пер
вую очередь мы встретим файл conf ig.h, который, подобно своему «тезке» сис
темного уровня include/minix/conf ig.h, должен включаться в файл исходно
го кода раньше локальных заголовочных файлов. Как и каталог include/
minix/, каталог src/kernel / включает файлы const .h и type .h. В include/
minix/ помещены файлы, используемые несколькими компонентами, в том чис
ле программами, работающими под управлением MINIX 3. Файлы, находящиеся
в каталоге src/kernel/, содержат определения, необходимые только для ком
пиляции ядра. Каталоги файловой системы, менеджера процессов и других ком
понентов системы также включают файлы const.h и type.h, определяющие
константы и типы для локального использования. В главный заголовочный файл
включены еще два файла, proto. h и gio. h; их аналогов нет в главных катало
гах include/, однако они присутствуют в файловой системе и менеджере про
цессов. Последним локальным заголовочным файлом, включенным в kernel .h,
является ip с . h.
Обратите внимание на начало файла kernel/conf ig.h. В нем вы найдете после
довательность #ifndef. . .# de fine, предотвращающую проблемы при попыт
ке неоднократного включения файла. Мы рассматривали это решение раньше,
однако обратите внимание, что на этот раз имя макроса, CONFIG_H, не начинает
ся с символа подчеркивания. Это означает, что макрос CONFIG_H отличается от
макроса _CONFIG_H, определенного в файле include/minix/conf ig.h.
Локальная версия файла config.h ядра объединяет определения, которые вам
вряд ли нужно изменять, если вы собираетесь использовать MINIX 3 для изуче
ния операционной системы или просто в качестве операционной системы компь
ютера общего назначения. Тем не менее представим себе, что вам необходимо
сделать систему MINIX 3 очень компактной, чтобы с ее помощью вести управле
ние научным инструментом или самодельным мобильным телефоном. Опре
деления, расположенные в строках 4717-4743, позволяют исключить ненужные
вызовы ядра. Избавление от ненужной функциональности также снижает требо
вания к памяти, поскольку код, осуществляющий обработку каждого вызова ядра,
условно компилируется при помощи упомянутых определений. При отключении
2.6. Реализация процессов в MINIX 3 175
Если вы получили такое сообщение, это означает, что инициализация ядра за
вершена. Функция prepare_shutdown (строка 7272) посылает всем процессам
сигнал SIGKSTOP (системным процессам нельзя посылать сигналы так же, как
пользовательским), а затем устанавливает таймер, выделяя всем системным про
цессам время на «уборку» до вызова последней процедуры, shutdown. Процеду
ра shutdown, как правило, возвращает управление монитору загрузки MINIX 3.
Для этого к контроллерам прерываний применяются параметры, восстанавли
ваемые из BIOS с помощью функции intr_init (0) (строка 7338).
IRQ 0 (часы)
IRQ 1 (клавиатура)
IRQ 3 (tty 2)
IRQ 4 (tty 1)
IRQ 5 (винчестер XT)
IRQ 6 (дисковод)
IRQ 7 (принтер)
Разница только в том, что при аппаратном прерывании адрес процедуры берется
не из памяти, а из регистра контроллера.
Механизм переключения задач в 32-разрядных процессорах Intel, как ответ на
прерывание, довольно сложен, и изменение значения счетчика команд — только
часть этого процесса. Когда процессор, уже обрабатывающий некоторый процесс,
получает прерывание, он сначала выделяет новый стек, который будет использо
ваться для выполнения обработчика. Положение стека определяется значением
записи в сегменте состояния задания (Task State Segment, TSS). Эта структура
едина для всей системы, инициализируется при вызове prot_init и модифи
цируется при запуске каждого процесса. В результате каждый новый стек, со
здаваемый прерыванием, всегда начинается от конца структуры stackframe_s
в записи в таблице процессов прерванного процесса. Затем процессор автомати
чески помещает в новый стек значения нескольких регистров, включая нужные
для восстановления стека и счетчика команд прерванного процесса. Обработчик
прерывания заносит в стек значения дополнительных регистров, заполняя кадр
стека, после чего переключается на другой стек, предоставляемый ядром. Этот
стек и используется для всех действий по обработке прерывания.
Завершив свою работу, обработчик прерывания переключается обратно на кадр
стека в таблице процессов (не обязательно на тот, который использовался для
предыдущего прерывания), затем самостоятельно извлекает из стека значения
2.6. Реализация процессов в MINIX 3 197
а б
Рис. 2.19. Сравнение механизмов обработки аппаратного прерывания и системного вызова:
а — обработка аппаратного прерывания; б — так происходит системный вызов
Рис. 2.20. Restart — код, исполняемый как после запуска системы, так и после прерываний
и системных вызовов. После него запускается следующий запланированный процесс
(как правило, отличный от прерванного). На диаграмме не показаны прерывания,
происходящие во время выполнения ядра
адрес возврата, помещенный в стек при вызове save. Если прерывание произош
ло во время выполнения пользовательского процесса, завершающая инструкция
iretd передает управление следующему процессу в очереди планировщика, при
этом восстанавливаются его оставшиеся регистры, в том числе указатель и адрес
сегмента стека. Но если управление было передано через restartl, то есть за
действован не кадр стека, а стек ядра, это означает, что после завершения совсем
не нужно переходить к новому процессу, а требуется завершить выполнение пре
рванного кода ядра. Процессор отслеживает подобную ситуацию, когда извлека
ет дескриптор сегмента кода из стека при выполнении iretd, и, обнаружив ее,
оставляет использоваться стек ядра.
Теперь самое время сказать несколько слов об исключениях. Исключения вы
зываются различными ошибками выполнения, однако это не всегда плохо. Ис
ключения полезны, чтобы побудить систему предоставить некоторые допол
нительные услуги, например выделить программе дополнительную память или
загрузить в оперативную память страницу, перемещенную в область подкачки
(хотя в MINIX 3 подобные услуги не предусмотрены). Иногда исключения обу
словлены ошибками в программах. Когда исключение возникает внутри ядра,
это вызывает серьезный сбой системы. Если исключение происходит в поль
зовательской программе, ее можно завершить, но такой подход неприменим
к операционной системе — она должна выполняться постоянно. Обрабаты
вают исключения так же, как и прерывания, то есть через дескрипторы в таб
лице дескрипторов прерываний. В этой таблице имеется шестнадцать запи
сей, содержащих указатели на точки входа обработчиков исключений, начиная
с _divide_error и заканчивая _copr_error, которые можно увидеть в кон
це файла трх386 . s (строки 6707-6769). Каждая из этих точек входа передает
управление процедуре exception (строка 6774) или errexception (стро
ка 6785) в зависимости от того, помещается ли при исключении в стек код
ошибки или нет. Обработка выполняется командами ассемблера и во многом на
поминает уже рассмотренный нами код: значения регистров сохраняются в сте
ке, и вызывается С-функция „exception (обратите внимание на знак под
черкивания перед именем). Последствия исключений могут быть различными:
некоторые игнорируются, некоторые вызывают сообщение о сбое ядра, другие
посылают сигналы процессам. Самой функцией „exception мы займемся в сле
дующем разделе.
Существует еще одна точка входа, которая обрабатывается как прерывание, —
это „level0_са11 (строка 6714). Она используется, когда код необходимо ис
полнить с нулевым (максимальным) уровнем привилегий. Эта точка входа нахо
дится в файле трх3 86. s вместе с прерываниями и исключениями потому, что
она также вызывается при помощи инструкции int <nnn>. Как и обработчики
исключений, она выполняет вызов save, а завершается инструкцией ret, веду
щей к „restart. Ее мы рассмотрим в следующем разделе, когда мы познако
мимся с кодом, нуждающемся в обычно недоступных (даже ядру) привилегиях.
Наконец, в конце ассемблерного файла выделяется место для хранения некото
рых данных. Определяются два различных сегмента данных.
206 Глава 2. Процессы
.sect.rom
Это объявление в строке 6822 гарантирует, что указанная область памяти нахо
дится в самом начале сегмента данных ядра. Сюда компилятор помещает сигна
туру (магическое число), чтобы программа boot могла убедиться, что загружает
действительно ядро. При компиляции всей системы за магическим числом будут
размещены различные строковые константы. Еще одна область данных задается
следующей директивой (строка 6825):
.sect.bss
отправить сообщение процессу 0. Если при этом процесс 4 также не сможет от
править сообщение процессу 0, возникает ситуация, продемонстрированная на
рис. 2.21, б.
очереди и помещенные в них процессы сразу после того, как ядро закончило
инициализацию и запустилось, то есть при вызове restart в файле main. с (стро
ка 7252). Массив rdy_head содержит по одному элементу для каждой очереди,
указывающему на первый процесс в очереди. Аналогично, элементы массива
rdy__ tail указывают на последние процессы в каждой очереди. Оба массива оп
ределены с макросом EXTERN в файле proc. h (строки 5595 и 5596). Начальное
формирование очередей процессов при запуске системы определяется таблицей
image в файле table. с (строки 6095-6109).
rdy_head rdy_tail
внутри области ядра, являются часы. А как же обстоит дело с драйверами уст
ройств, располагающих собственными адресными пространствами?
Ответ на данный вопрос состоит в том, что их обработкой занимается системное
задание. На самом деле, системное задание занимается практически всем, что ка
сается взаимодействия между ядром и процессами, находящимися в пользова
тельском пространстве. При необходимости регистрации обработчика прерыва
ний «пользовательский» драйвер устройства обращается к системному заданию
с вызовом sys_irqctl. Системное задание вызывает функцию put_irq_
handler, однако в поле обработчика прерываний вместо адреса, принадлежаще
го пространству драйвера, сохраняется адрес процедуры genetic_handler, яв
ляющейся частью системного задания. Поле номера процесса в структуре irq_
hook используется процедурой generic_handler для доступа к записи драй
вера в таблице привилегий и установки соответствующего бита в карте актив
ных прерываний. Затем generic_handler посылает драйверу уведомление от
имени HARDWARE, в которое включается битовая карта активных уведомлений
драйвера. Таким образом, если драйверу необходимо отвечать на прерывания бо
лее чем одного источника, он может определить, какое из прерываний вызвало
передачу уведомления. Фактически установка битовой карты предоставляет
драйверу информацию обо всех активных прерываниях. Еще одно поле в струк
туре irq_hook — поле политики, определяющее, следует разрешить прерывание
немедленно или оставить его запрещенным. Во втором случае драйвер сам дол
жен будет выполнить вызов ядра sys_irqenable по завершении обработки
прерывания.
Одна из целей создания операционной системы MINIX 3 — поддержка реконфи
гурирования устройств ввода-вывода в процессе выполнения. Следующая функ
ция, rm_irq_handler, удаляет обработчик, что является необходимым при
удалении и замене драйвера устройства. Фактически rm_irq_handler выпол
няет действия, противоположные put_irq_handler.
Последняя функция файла 18259. с — функция intr_handle (строка 8221);
она вызывается из макросов hwint_master и hwint_slave, знакомых нам по
файлу mpx386.s. Элемент массива irq_actids битовых карт, соответствую
щих обслуживаемому прерыванию, используется для регистрации текущего со
стояния каждого обработчика в списке. Для каждой функции списка intr_
handle устанавливает соответствующий бит irq_actids и вызывает обработ
чик. Если обработчик не выполняет каких-либо действий либо завершает обслу
живание прерывания немедленно, он возвращает значение TRUE, и бит в irq_
actids очищается. В конце макросов hwint_master и hwint_slave битовая
карта прерывания, рассматриваемая как целое число, проверяется, чтобы опре
делить, можно ли разрешить прерывание перед запуском следующего процесса.
Она копирует блок данных из одной области физической памяти в любое другое
место. Оба передаваемых этой функции адреса абсолютные, то есть 0 означает
первый байт адресного пространства. Все три аргумента функции являются по
типу беззнаковыми длинными целыми.
В целях безопасности вся используемая программой память должна быть очищена
от любых данных, оставленных в ней предыдущей программой. В MINIX 3 эта за
дача решается с помощью системного вызова ехес, в конечном счете, обращаю
щегося к функции phys_memset (строка 9248) — следующей в файле klib386 . s.
Две следующие короткие функции специфичны для процессоров Intel. Функция
_mem_rdw (строка 9291) возвращает 16-разрядное слово из произвольного места
памяти. Результат дополняется нулями и помещается в 32-разрядный регистр
еах. Функция _reset (строка 9307) сбрасывает процессор. Для этого в деск
риптор прерываний процессора загружается нулевой указатель, после чего вы
зывается программное прерывание. Результирующий эффект равносилен аппа
ратному сбросу.
2.7. Системное задание в MINIX 3 221
Таблица 2,5. Типы сообщений, принимаемых системным заданием. Тип Any означает
любой системный процесс. Пользовательские процессы не могут обращаться
к системному заданию напрямую
Тип сообщений Источник Описание
sys_fork pm Процесс запущен
sys_exec pm Установить указатель стека после вызова ехес
sys_exit pm Процесс завершен
sys_nice pm Задать приоритет планирования
sys_p rivctl rs Задать или изменить привилегии
sys_trace pm Выполнить операцию вызова ptrace
sys_kill pm, fs, tty Послать сигнал процессу после вызова kill
sys_getksig pm Проверка активных сигналов менеджером процессов
sys_endksig pm Завершение обработки сигнала менеджером процессов
sys_sigsend pm Послать сигнал процессу
sys_sig return pm Очистка после завершения сигнала
sysjrqctl Драйверы Разрешить, запретить или сконфигурировать
прерывание
sys_devio Драйверы Выполнить операцию чтения или записи над портом
sys_sdevio Драйверы Выполнить над портом операцию чтения или записи
строки
sys_vdevio Драйверы Выполнить вектор запросов ввода-вывода
sys_int86 Драйверы Выполнить вызов BIOS в реальном режиме
sys_newmap pm Установить карту памяти процесса
sys_segctl Драйверы Добавить сегмент и получить его селектор (доступ
к удаленным данным)
sysjriemset pm Записать символ в область памяти
sys_umap Драйверы Преобразовать виртуальный адрес в физический
sys_vircopy fs, драйверы Выполнить копирование с использованием
виртуальной адресации
sys_physcopy Драйверы Выполнить копирование с использованием
физической адресации
sys_virvcopy Any Вектор запросов vcopy
sys_physvcopy Any Вектор запросов physcopy
sys_times pm Получить время работы системы и процессов
sys_setalarm pm, fs, драйверы Запланировать сигнал синхронизации
sys_abort pm, tty Сбой: MINIX не может продолжать работу
sys_getinfo Any Запросить системную информацию
Для начала опишем назначение каждого вызова ядра. Типы сообщений в табл. 2.5
можно разделить на категории. Первые несколько сообщений относятся к управле
нию процессами. Очевидно, что вызовы sys_fork, sys_exec, sys_exit и sys_
trace тесно связаны с системными вызовами стандарта POSIX. Хотя nice не
относится к числу обязательных вызовов POSIX, изменение приоритета процес
са, в конечном счете, приводит к вызову sys_nice ядра. Единственным незна
комым вам вызовом из данной группы может быть лишь вызов sys_privctl.
2.7. Системное задание в MINIX 3 225
Рис. 2.24. Доставка данных по системному вызову read: а — худший вариант чтения блока
требует 11 сообщений, б — лучший вариант чтения блока требует 4 сообщения
Кварцевый осциллятор
Время суток
в секундах
Число тиков
в текущую
секунду
1 /
Время загрузки
системы в секундах
в
Рис. 2.26. Три варианта поддержки текущего времени
Таймерное задание
Главный цикл таймерного задания принимает единственный тип сообщений,
HARD_INT, поступающих от обработчика прерываний. Все остальные сообщения
считаются ошибочными. Более того, главный цикл принимает сообщения не для
всех прерываний от таймера, хотя подпрограмма приема сообщений называется
do_clocktick. Последняя вызывается только при необходимости планирова
ния процесса или при истечении таймера.
240 Глава 2. Процессы
Сторожевые таймеры
Несколько страниц назад мы оставили открытым вопрос о том, как обеспечить
пользовательские процессы сторожевыми таймерами, которые обычно представ
ляют как пользовательские процедуры, исполняемые по истечении таймера.
Очевидно, в MINIX 3 это попросту невозможно. Тем не менее мы можем постро
ить мост между ядром и пользовательским пространством с помощью сигналов
синхронизации.
Сейчас самое время разобраться в том, что представляет собой сигнал синхрони
зации. Сигнал может генерироваться вне зависимости от того, какой процесс ис
полняется в текущий момент. То же касается и активации обычного сторожевого
таймера. Говорят, что такие события происходят асинхронно. Сигнал синхрони
зации доставляется в виде сообщения и может быть получен при выполнении
адресатом вызова receive. Мы говорим о синхронности потому, что адресат ожи
дает сигнал. Когда отправитель уведомляет адресата о сигнале с помощью проце
дуры notify, отправителю не нужно блокироваться, а адресату — беспокоиться
2.8. Таймерное задание в MINIX 3 241
о том, что сигнал будет пропущен. Если адресат не находится в ожидании, сооб
щения процедуры notify сохраняются. Для этой цели используется битовая
карта, где каждый бит соответствует возможному отправителю.
Сторожевые таймеры используют поле s_alarm_timer типа timer_t, имею
щееся в каждом элементе таблицы привилегий. Каждому системному процессу
в этой таблице соответствует свой элемент. Чтобы установить таймер, систем
ный процесс, находящийся в пользовательском пространстве, выполняет вызов
sys_setalarm, обрабатываемый системным заданием. Системное задание на
ходится в пространстве ядра и, таким образом, может инициализировать таймер
от имени вызывающего процесса. Инициализация включает размещение в нуж
ном поле адреса процедуры, запускаемой при истечении таймера, и ввод таймера
в список таймеров, как показано на рис. 2.27.
Разумеется, исполняемая процедура также должна находиться в пространстве
ядра. Это не является проблемой. Системное задание располагает сторожевой
функцией, cause_alarm, генерирующей пользователю синхронное уведомле
ние в момент срабатывания с помощью процедуры notify. Данный сигнал мо
жет вызвать сторожевую функцию в пользовательском пространстве. «Настоя
щая» сторожевая функция находится внутри ядра, а пользовательский процесс
всего лишь получает сигнал синхронизации. Подобный алгоритм отличается от
исполнения пользовательской функции таймером. Он сопровождается больши
ми накладными расходами, однако проще, чем механизм прерываний.
Мы намеренно указали на то, что системное задание устанавливает сигналы от
имени некоторых процессов пользовательского пространства. Описанный меха
низм применяется лишь к системным процессам. У каждого системного процес
са есть собственная запись в таблице привилегий, а все несистемные процессы
используют единственную общую запись. Области таблицы привилегий, кото
рые не могут использоваться совместно, например битовая карта активных уве
домлений и таймер, недоступны пользовательским процессам вообще. Выход из
ситуации следующий: менеджер процессов управляет таймерами от имени поль
зовательских процессов. Аналогичным образом системное задание управляет
таймерами от имени системных процессов.
Когда пользовательский процесс выполняет системный вызов alarm, чтобы
установить сигнал, он обрабатывается менеджером процессов, конфигурирую
щим таймер и помещающим его в список таймеров. Менеджер процессов просит
системное задание послать ему уведомление, когда для первого таймера в списке
будет назначено время истечения. Помощь менеджеру процессов требуется лишь
при изменении начального элемента списка таймеров — из-за того, что первый
таймер истек или был выключен, либо из-за нового вызова, таймер которого дол
жен оказаться в списке первым. Все это делается для поддержки системного
вызова alarm, определенного стандартом POSIX. Подлежащая исполнению
процедура находится в адресном пространстве менеджера процессов. Во время
выполнения инициировавшему вызов пользовательскому процессу передается
сигнал, а не уведомление.
242 Глава 2. Процессы
Миллисекундные задержки
В файле clock, с имеется процедура, обеспечивающая микросекундное разре
шение. Различным устройствам ввода-вывода требуются задержки длительно
стью всего лишь несколько микросекунд, которые невозможно обеспечить на
практике при помощи сигналов и интерфейса передачи сообщений. Счетчик,
используемый для генерации прерываний от таймера, может быть считан впрямую.
Его уменьшение выполняется с интервалом, приблизительно равным 0,8 мкс,
а значение достигает нуля 60 раз в секунду (один раз за 16,67 мс). Для того
чтобы использовать значение счетчика для задержек устройств ввода-вывода,
его необходимо опрашивать процедурой, находящейся в пространстве ядра, од
нако драйверы устройств размещены за его пределами. В настоящее время эта
функция используется только в качестве источника исходных данных для ге
нератора случайных чисел. Возможно, она окажется более полезной в системе
с высоким быстродействием, но это — вопрос будущего.
Службы времени
В табл. 2.6 перечислены службы файла clock, с, прямо или косвенно предостав
ляющие различные услуги. Несколько функций объявлены открытыми (public),
что дает возможность вызывать их из ядра или системного задания. Все остальные
службы доступны только косвенно — с помощью системных вызовов, в конеч
ном счете, обрабатываемых системным заданием. Остальные системные процес
сы могут косвенно обращаться к системному заданию. Пользовательские про
цессы должны взаимодействовать с менеджером задач, который также прибегает
к службам системного задания.
Ядро и системное задание могут получить текущее время работы системы, уста
новить или сбросить таймер, не прибегая к затратному обмену сообщениями.
Ядро и системное задание также вызывают функцию read_clock, считываю
щую счетчик из микросхемы таймера для получения времени, выраженного в еди
ницах длительностью приблизительно 0,8 мкс. Функция clock_stop вызывает
ся при завершении работы MINIX 3 и восстанавливает тактовую частоту BIOS.
2.8. Таймерное задание в MINIX 3 243
Резюме
Чтобы скрыть эффект прерываний, операционная система предоставляет кон
цептуальную модель, в которой параллельно выполняются логически упорядо
ченные процессы. Процессы могут взаимодействовать друг с другом при помощи
примитивов, таких как семафоры, мониторы и сообщения. Назначение примити
вов в том, чтобы гарантировать, что никакие два процесса не окажутся в крити
ческой секции единовременно. Процессы могут находиться в состоянии вы
полнения, готовности и приостановки, а также переходить из одного состояния
в другое, когда тот или иной процесс исполняет один из примитивов взаимодей
ствия между процессами.
Примитивы необходимы для решения таких проблем, как проблема производи
теля и потребителя, проблема обедающих философов, проблема писателей и чи
тателей. Но даже при использовании примитивов необходимо быть осторожным,
во избежание ошибок и взаимных блокировок. Известно много различных алго
ритмов планирования, таких как циклический алгоритм, приоритетный алго
ритм, алгоритм с многоуровневыми очередями, алгоритм управления политика
ми планирования.
Операционная система MINIX 3 поддерживает концепцию процесса и предо
ставляет примитивы для взаимодействия между процессами. Сообщения не бу
феризуются, поэтому вызов send завершается успехом только после того, как
адресат получит сообщение. Аналогично, вызов receive завершается лишь
тогда, когда сообщение уже отправлено. В противном случае сделавший вызов
процесс переходит в состояние ожидания. MINIX 3 также поддерживает небло
кирующую разновидность сообщений — уведомления, передаваемые с помощью
примитива notify. Попытка послать уведомление адресату, не находящемуся
в состоянии ожидания, приводит к установке бита, вызывающего восстановле
ние уведомления при вызове receive в будущем.
В качестве примера потока сообщений рассмотрим вызов read, выполняемый
пользовательским процессом. Процесс посылает запрос на чтение в виде сообще
ния, адресованного файловой системе. Если данные не удалось обнаружить в кэше,
файловая система запрашивает их чтение с диска у драйвера, а сама переходит
в состояние блокировки. При дисковом прерывании системное задание получает
уведомление, позволяющее ему передать ответ драйверу диска, а тому, в свою
очередь, — файловой системе. После этого файловая система запрашивает систем
ное задание скопировать данные из кэша, в который был помещен требуемый
блок, пользовательскому процессу. Перечисленные шаги иллюстрирует рис. 2.24.
За прерыванием может последовать переключение процессов. В случае прерывания
в элементе таблицы процессов, соответствующем прерванному процессу, созда
ется стек, в который помещается вся информация, необходимая для его переза
пуска. При перезапуске процесса указателю стека присваивается адрес элемента
таблицы процессов, далее выполняются команды, восстанавливающие регистры
процессора, а затем — команда iretd. Выбор элемента таблицы процессов, адрес
которого помещается в указатель стека, возлагается на планировщика.
246 Глава 2. Процессы
Вопросы и задания
1. Почему многозадачность является основным требованием для современных
операционных систем?
2. Каковы три основных состояния процесса? Кратко опишите смысл каждого
из них.
3. Представьте, что вы разрабатываете усовершенствованную компьютерную
архитектуру, в которой процессы переключаются аппаратно, а не с помощью
прерываний. Какая информация потребуется процессору? Опишите возмож
ную реализацию аппаратного переключения процессов.
4. На всех существующих компьютерах как минимум часть обработчиков пре
рываний написана на ассемблере. Почему?
5. Измените рис. 2.2, добавив в него два состояния процесса — «новый» и «за
вершенный». Процесс находится в состоянии «новый» после создания, а в со
стоянии «завершенный» — после окончания своей работы.
Вопросы и задания 247
уже висят несколько обезьян, движущихся на запад, бабуин ждет, когда ве
ревка освободится. Другое условие: нужно запретить для остальных движе
ние на запад, пока бабуин не перейдет на восток.
43. Решите задачу обедающих философов с помощью мониторов, а не семафоров.
44. Добавьте в ядро MINIX 3 код, который бы подсчитывал, сколько раз процесс
(или задание) i обратится к процессу (или заданию) j. Сделайте так, чтобы
полученная матрица печаталась по нажатию клавиши F4.
45. Измените планировщик MINIX 3 так, чтобы он отслеживал, сколько времени
каждый процесс занимал процессор последний раз. Когда все задания и сер
веры освобождают процессор для пользовательских процессов, выбирайте
тот из них, который меньше всех тревожил процессор.
46. Измените MINIX 3 так, чтобы каждый процесс мог явно задавать приори
тет своих дочерних процессов с использованием нового системного вызова
setpriority с параметрами pid и priority.
47. Перепишите макросы hwint_master и hwint_slave из файла mpxx386 . s
так, чтобы действия, выполняемые функцией save, были встроены в код. На
сколько увеличился объем кода? Можете ли вы измерить прирост производи
тельности?
48. Поясните все выводимые элементы команды sysenv системы MINIX 3. Если
в вашем распоряжении нет работающей копии операционной системы, вос
пользуйтесь листингом 2.14.
49. Обсуждая инициализацию таблицы процессов, мы упомянули, что некоторые
компиляторы С могут генерировать несколько лучший код при сложении ад
реса массива с константой, нежели при индексировании. Напишите пару ко
ротких С-программ, чтобы проверить это.
50. Измените систему MINIX 3 так, чтобы в ней осуществлялся сбор сведений
о передаче сообщений с указанием отправителей и получателей. Напишите
программу, выполняющую подобный сбор и печатающую статистику в удоб
ном для восприятия виде.
Глава 3
Ввод-вывод
Одна из важнейших функций операционной системы состоит в управлении
всеми устройствами ввода-вывода компьютера. Операционная система должна
давать этим устройствам команды, перехватывать прерывания и обрабатывать
ошибки. Она должна также предоставить простой и удобный интерфейс между
устройствами и остальной частью системы. Интерфейс, насколько это возможно,
должен быть одинаковым для всех устройств (он не должен зависеть от при
меняемого оборудования). Программное обеспечение ввода-вывода составляет
существенную часть операционной системы. Тому, как операционная система
управляет устройствами ввода-вывода, и посвящена эта глава.
Глава организована следующим образом. Сначала мы рассмотрим некоторые
базовые понятия, касающиеся устройств ввода-вывода, а затем в общих чертах
познакомимся с соответствующим программным обеспечением. Программное
обеспечение ввода-вывода может быть структурировано в виде уровней, каждо
му из которых отведен строго очерченный круг задач. Мы рассмотрим все уров
ни, чтобы понять, что они делают и как согласуются друг с другом.
Далее следует раздел, посвященный взаимной блокировке. Мы дадим строгое
определение этому понятию, покажем, как возникают взаимные блокировки,
представим две модели для их анализа и обсудим некоторые алгоритмы их пре
дотвращения.
Затем мы сделаем общий обзор механизма ввода-вывода в MINIX 3, в том числе
прерываний, драйверов устройств, зависимого и независимого от оборудования
ввода-вывода. Далее мы детально изучим различные устройства ввода-вывода:
диски, клавиатуры и дисплеи. Каждое устройство будет рассмотрено как с аппа
ратной, так и с программной точки зрения.
Таблица 3.1. Скорости передачи данных некоторых типичных устройств, шин и сетей
Устройство Скорость
Клавиатура 10 байт/с
Мышь 100 байт/с
Модем 56 Кбайт 7 Кбайт/с
Сканер 400 Кбайт/с
Цифровая камера 4 М байт/с
CD-ROM 52х 8 Мбайт/с
Firewire (IEEE 1394) 50 Мбайт/с
USB 2.0 60 Мбайт/с
Монитор XGA 60 Мбайт/с
Сеть SONET ОС-12 78 Мбайт/с
Gigabit Ethernet 125 Мбайт/с
Диск Serial ATA 200 Мбайт/с
Диск SCSI Ultrawide 320 Мбайт/с
Шина PCI 528 Мбайт/с
Монитор
Шина
Рис. 3.1. Модель подключения процессора, памяти, контроллеров и устройств ввода-вывода
3.1.4. Прерывания
Как правило, регистры контроллеров содержат один или несколько битов состоя
ния. Их можно проверить и определить, завершена ли операция вывода и имеются
ли новые данные в устройстве ввода. Цикл, выполняемый процессором и прове
ряющий бит состояния до готовности устройства принять или передать данные,
называется опросом, или активным ожиданием. Мы познакомились с этой кон
цепцией в пункте 2.2.3 в контексте работы с критическими секциями (впрочем,
в большинстве случаев активное ожидание нежелательно). Поскольку ожидание
258 Глава 3. Ввод-вывод
Эта команда должна работать, невзирая на то, что именно указано в качестве
входного устройства — гибкий диск, IDE-диск, SCSI-диск или клавиатура. В ка
честве выходного устройства также с равным успехом может быть указан экран,
файл на любом диске или принтер. Все проблемы, связанные с отличиями этих
устройств, должна разрешать операционная система.
Тесно связан с идеей независимости от устройств принцип единообразного име
нования. Имя файла или устройства должно быть просто текстовой строкой
или целым числом и никоим образом не зависеть от физического устройства.
В UNIX и MINIX 3 все диски могут быть произвольным образом интегрированы
в иерархию файловой системы, поэтому пользователю не обязательно знать,
какое имя какому устройству соответствует. Например, гибкий диск не запреща
ется монтировать поверх каталога /usr/ast/backup, вследствие чего копи
рование файла в каталог /usr/ast/backup/monday автоматически приведет
к копированию файлов на гибкий диск. Таким образом, все файлы и устройства
адресуются одним и тем же способом — по пути к ним.
Другим важным аспектом программного обеспечения ввода-вывода является
обработка ошибок. Ошибки должны обрабатываться как можно ближе к аппа
ратуре. Если контроллер обнаружил ошибку чтения, он должен попытаться по
возможности исправить эту ошибку сам. Если он не в силах это сделать, тогда
ошибку обязан обработать драйвер устройства, допустим, попытавшись прочи
тать этот блок еще раз. Многие ошибки бывают временными, например ошибки
чтения, вызванные пылинками на читающих головках. Такие ошибки часто не
воспроизводятся при повторной попытке чтения блока. Только если нижний
262 Глава 3. Ввод-вывод
то, что система поддерживает несколько типов мышей. Дисковый драйвер мог бы
поддерживать несколько типов дисков, различающихся объемами и скоростями,
а также, возможно, компакт-диски. С другой стороны, диск настолько непохож
на мышь, что им, безусловно, нужны разные драйверы.
Чтобы драйвер имел доступ к аппаратной части устройства, то есть к регистрам
контроллера, его традиционно интегрируют в ядро операционной системы. Та
кой подход обеспечивает максимальную производительность, но минимальную
надежность, поскольку ошибка в любом драйвере устройства способна вывести
из строя всю систему. В MINIX 3 используется другая, более надежная модель.
Как мы увидим, в этой операционной системе каждый драйвер устройства явля
ется отдельным процессом, выполняющимся в пользовательском пространстве.
Как мы говорили ранее, с точки зрения операционной системы бывают драйверы
для блочных устройств (например, диски) и символьных устройств (например,
клавиатуры и принтеры). Большинство операционных систем определяет два
стандартных интерфейса, которые должны поддерживаться всеми блочными
и всеми символьными устройствами компьютера соответственно. Интерфейсы
включают совокупность процедур, вызываемых операционной системой, чтобы
обеспечить драйверам возможность выполнять свою работу.
Вообще говоря, назначение драйвера в том, чтобы воспринимать абстрактные за
просы от аппаратно-независимых программ верхнего уровня и сообщать им, что
запрос выполнен. Типичный запрос, поступающий драйверу диска, — считать за
данный блок данных. При этом если в момент передачи запроса драйвер бездей
ствует, он сразу начинает работу. Если же драйвер занят, запрос обычно помеща
ется в очередь и обслуживается по мере возможности.
Первым шагом в обслуживании запроса ввода-вывода является проверка кор
ректности переданных параметров и при необходимости возврат ошибки. Если
запрос верен, следующий шаг — его преобразование из абстрактного представле
ния в конкретную форму. Скажем, драйвер диска должен выяснить, где находит
ся запрошенный блок данных, проверить, работает ли привод диска, находится
ли головка над нужной дорожкой и т. д. Говоря коротко, драйвер должен сам оп
ределить свою последовательность действий.
После того как необходимые команды определены, драйвер начинает передавать
их устройству через регистры контроллера. Простые контроллеры способны вос
принимать только по одной команде за раз, а более сложные поддерживают свя
занный список команд, выполняемых далее без вмешательства операционной
системы.
Когда все команды переданы, ситуация развивается по одному из двух сценари
ев. Во многих случаях драйвер устройства должен ждать, пока контроллер вы
полняет для него определенную работу, поэтому он блокируется до поступления
прерывания от устройства. В других вариантах операция завершается без за
держек, и драйверу не нужно блокироваться. Например, для прокрутки экрана
в символьном режиме требуется записать лишь несколько байтов в регистры
контроллера. Каких-либо физических перемещений нет, и вся операция занима
ет несколько микросекунд.
3.2. Программное обеспечение ввода-вывода 265
Буферизация
Буферизация также является важной как для блочных, так и для символьных
устройств. Для блочных устройств аппаратное обеспечение обычно требует, чтобы
чтение или запись производились большими блоками. Однако для пользователь
ских программ такого ограничения нет, и они вправе передавать любые объемы
информации. Поэтому если пользователь передает только половину блока, опе
рационная система обычно не сразу записывает эти данные на диск, а дожидает
ся передачи оставшейся части блока. Что же касается символьных устройств, то
пользователь может передавать данные быстрее, чем устройство в состоянии их
воспринять, таким образом, и здесь нужна буферизация. Не исключено также,
что данные, поступающие, например, от клавиатуры, могут опережать считыва
ние, и в этом случае также не обойтись без буфера.
Сообщения об ошибках
В контексте ввода-вывода ошибки — как нигде частое явление. Операционная
система должна приложить максимальные усилия к их обработке. Многие ошибки
являются специфичными для конкретного устройства и должны обрабатываться
драйвером, так как только он знает, что делать (например, повторить попытку,
игнорировать ошибку или инициировать сбой системы). Типичная ошибка — по
вреждение или недоступность блока на диске. Драйвер диска пытается несколь
ко раз повторить чтение и, если оно не удается, информирует вышестоящую про
грамму. С этого момента обработка ошибки является аппаратно-независимой.
Если ошибка имела место при чтении пользовательского файла, достаточно про
сто передать сообщение программе, сделавшей вызов. Если же невозможно про
читать критически важную системную структуру, не исключено, что системе
придется вывести информацию об ошибке и завершить свою работу.
Ответ
ввода-вывода Функции
Уровень / ввода-вывода
Обращение к вызовам ввода-вывода;
Процесс пользователя
А форматный ввод-вывод; спулинг
▼Устройство-независимое 1 Именование, защита, блокирование,
программное обеспечение буферизация, назначение
п -
1к Драйверы -
устройства
1 Установка регистров устройств;
завершение операции ввода-вывода
т
Активизировать драйвер по завершении
Обработчики прерываний
операции ввода-вывода
, I
' Аппаратура Выполнение операции ввода-вывода
3.3.1. Ресурсы
Система может оказаться в ситуации взаимной блокировки, когда процессам
предоставляются исключительные права доступа к устройствам, файлам и т. д.
Чтобы максимально обобщить рассказ о взаимных блокировках, мы будем назы
вать объекты доступа ресурсами. Ресурсом может быть устройство (например,
3.3. Взаимная блокировка 271
Для того чтобы произошла взаимная блокировка, должны выполниться все эти
четыре условия. Если хоть одно из них не выполняется, тупиковая ситуация не
возможна.
В серии публикаций Левина [76—78] указывается на то, что в литературе тер
мином «взаимная блокировка» называют целый ряд ситуаций и перечисленные
в [21] условия относятся лишь к взаимной блокировке ресурсов. Известны приме
ры взаимных блокировок, не удовлетворяющих всем четырем сформулирован
ным условиям. Например, если четыре автомобиля одновременно встретятся на
перекрестке, то, согласно правилам, ни один из них не вправе продолжить дви
жение, однако здесь ни один из «процессов» (автомобилей) не является владель
цем уникального ресурса. Данная проблема называется взаимной блокировкой
планирования и разрешается внешним участником — полицейским, принимаю
щим решение о приоритетах машин.
Следует заметить, что каждое из условий относится к политике, которая может
быть принята или не принята в системе. Может ли определенный ресурс едино
временно использоваться более чем одним устройством? Выгружаемы ли ресур
сы? Возможно ли циклическое ожидание? Позже мы увидим, как справиться
с взаимными блокировками, нарушая некоторые из этих условий.
a б в
Рис. 3.6. Графы распределения ресурсов: а — ресурс занят; б — запрос ресурса;
в — взаимная блокировка
А В С
Запрос R Запрос S Запрос Т
Запрос S Запрос Т Запрос R
Освобождение R Освобождение S Освобождение Т
Освобождение S Освобождение Т Освобождение R
а б в
1. А запрашивает R
2. В запрашивает S @ ©
3. С запрашивает Т
4. А запрашивает S
5. В запрашивает Т
6. С запрашивает R
S Т
Взаимоблокировка
2 Ж
3 U К
1. А запрашивает R
2. С запрашивает Т
3. А запрашивает S
4. С запрашивает R
5. А освобождает R
6. А освобождает S
Взаимоблокировки нет R S Т
л н о
п с
Рис. 3.7. Пример возникновения взаимной блокировки и способ избежать ее
276 Глава 3. Ввод-вывод
Первая проблема, вносимая этим подходом, заключается в том, что многие про
цессы не знают, сколько ресурсов им понадобится, до тех пор, пока не начнут ра
боту. Другая проблема состоит в том, что ресурсы не будут расходоваться опти
мально. Возьмем, например, процесс, который читает данные с входной ленты,
анализирует их в течение часа и затем пишет выходную ленту, а заодно и чертит
результаты на плоттере. Если все ресурсы нужно запрашивать заранее, то про
цесс целый час не позволит работать накопителю на магнитной ленте и принтеру.
Слегка отличающийся метод, позволяющий нарушить условие удержания и ожи
дания, вытекает из наложения следующего требования на процесс, запрашиваю
щий ресурс: процесс сначала должен временно освободить все используемые им
в данный момент ресурсы. Затем этому процессу разрешается попытаться сразу
получить все необходимое.
Попытка исключить третье условие (нет принудительной выгрузки ресурса)
вселяет еще меньше надежд, чем устранение второго условия. Если процесс по
лучил принтер и в данный момент печатает выходные данные, насильственное
изъятие принтера по причине недоступности требуемого плоттера в лучшем слу
чае сложно, а в худшем — невозможно.
Остается только одно условие. Циклическое ожидание можно устранить несколь
кими путями. Один их них — просто следовать правилу, гласящему, что процессу
дано право только на один ресурс в конкретный момент времени. Если нужен вто
рой ресурс, процесс обязан освободить первый. Но подобное ограничение непри
емлемо для процесса, копирующего огромный файл с магнитной ленты на принтер.
Другой способ обойти циклическое ожидание заключается в поддержке общей
нумерации всех ресурсов, как показано на рис. 3.8, а. В этом случае действует
следующее правило: процессы могут запрашивать ресурс, когда хотят этого, но
все запросы должны быть сделаны в соответствии с нумерацией ресурсов. Про
цесс может запросить сначала сканер, затем накопитель на магнитной ленте, но
не вправе сначала потребовать плоттер, а затем сканер.
1. Фотонаборное устройство
2. Сканер
3. Плоттер
4. Ленточный накопитель
5. Накопитель CD-ROM
а
Рис. 3.8. Общая нумерация ресурсов: а — пронумерованные ресурсы; б — граф ресурсов
так как этот номер меньше номера уже занятого им ресурса. Так или иначе, вза
имная блокировка исключена.
При работе с несколькими процессами сохраняется та же самая логика. В каж
дый момент времени один из предоставленных ресурсов будет иметь наивысший
номер. Процесс, использующий этот ресурс, уже никогда не запросит другие
занятые ресурсы. Он либо закончит свою работу, либо, в худшем случае, запро
сит ресурс с еще большим номером, а любой такой ресурс является доступным.
В итоге процесс завершит работу и освободит свои ресурсы. На этот момент сло
жится ситуация, когда ресурс с высшим номером уже занят каким-то другим
процессом, который также сможет нормально завершиться. То есть существует
алгоритм, по которому все процессы отрапортуют о выполнении без взаимной
блокировки.
Вариантом этого алгоритма является схема, в которой исключается требование
приобретения ресурсов в строго возрастающем порядке, но сохраняется условие,
что процесс не может запросить ресурсы с меньшим номером, чем уже у него
имеющиеся. Если процесс на начальной стадии запрашивает ресурсы 9 и 10, за
тем освобождает их, то это равнозначно тому, как если бы он начал работу зано
во, поэтому нет причины запрещать ему запрос ресурса 1.
Хотя систематизация ресурсов путем их нумерации устраняет проблему взаимных
блокировок, бывают ситуации, когда невозможно найти порядок, удовлетворяю
щий всех. Когда ресурсы включают в себя области таблицы процессов, дисковое
пространство для спулинга, закрытые записи базы данных и другие абстрактные
ресурсы, число потенциальных объектов интереса и вариантов их применения
может быть настолько огромным, что никакая систематизация не спасет. Кроме
того, согласно [78], упорядочивание ресурсов сводит «на нет» взаимозаменяе
мость — копия ресурса при таких правилах может оказаться недоступной.
В табл. 3.2 обобщены различные методы предотвращения взаимных блокировок.
a б в
Рис. 3.9. Три состояния распределения ресурсов: а — безопасное;
б — безопасное; в — небезопасное
Траектории ресурсов
Предыдущий алгоритм описан в терминах одного класса ресурсов (то есть в на
шем распоряжении имеются только принтеры или только ленточные накопители,
но не то и другое вместе). На рис. 3.10 представлена модель системы с двумя про
цессами и двумя классами ресурсов, например принтером и плоттером. По гори
зонтальной оси выводятся номера команд, выполняемых процессом А. По верти
кальной оси отложены номера команд, выполняемых процессом В. В команде Ц
процесс А запрашивает принтер, в команде 12 ему требуется плоттер. Принтер
и плоттер освобождаются командами /3 и Ц соответственно. Процессу В необхо
дим плоттер с команды /5 по команду /7 и принтер с команды /6 по команду /8.
Каждая точка на диаграмме представляет совместное состояние двух процес
сов. Изначально система находится в точке р, когда ни один из процессов еще
не выполнил ни одной инструкции. Если планировщик запустит процесс А пер
вым, мы попадем в точку q, в которой процесс А выполнил какое-то количество
команд, а процесс В еще ничего не сделал. В точке q траектория становится
вертикальной, показывая, что планировщик решил запустить процесс В. При
наличии одного процессора все отрезки траектории могут быть только верти
кальными или только горизонтальными, но не наклонными. Кроме того, движе
ние всегда происходит на «север» или «восток» (вверх или вправо) и никогда
на «юг» или «запад» (вниз или влево), так как процессы не могут течь в обрат
ном направлении.
3.3. Взаимная блокировка 283
Принтер Ч-
Плоттер
Рис. 3.10. Две траектории ресурсов для процессов
Если во время первой фазы какая-либо необходимая запись оказывается уже за
блокированной, процесс просто сбрасывает все свои блокировки и начинает пер
вую фазу заново. В некотором смысле этот метод похож на схему, в которой за
прос всех необходимых ресурсов происходит загодя или, по крайней мере, перед
тем, как произойдет что-то необратимое. В некоторых вариациях двухфазной
блокировки, если блокировка встретилась во время первой фазы, не происходит
возврата ресурсов и возобновления работы процесса. В таких вариациях может
возникнуть тупиковая ситуация.
Но эту стратегию нельзя обобщить. Например, в системах реального времени
и системах контроля процессов недопустимо частично завершить процесс из-за
того, что ресурс недоступен, а потом начинать все заново. Также недопустимо
перезапускать процесс, если он прочел сообщение из сети или написал его, обно
вил файлы и сделал что-нибудь еще, что не может быть безопасно повторено.
Алгоритм работает только в тех ситуациях, когда программист очень тщательно
подготовил все таким образом, что программу нетрудно остановить в любой точ
ке первой фазы и запустить заново. Многие программы не могут быть структу
рированы таким образом.
Кроме того, для групп сходных устройств, например для дисков и терминалов,
есть еще и заголовочные файлы. Файл driver.h поддерживает все драйверы
блочных устройств, а файл tty.h предоставляет общие определения для всех
типов терминалов.
Поскольку система MINIX 3 построена на исполнении компонентов операционной
системы в пользовательском пространстве как полностью независимых процессов,
для нее характерны высокая степень модульности и приемлемая эффективность.
Это — одно из кардинальных отличий MINIX 3 от UNIX. В MINIX процесс, для того
чтобы прочитать файл, посылает сообщение файловой системе. В свою очередь,
файловая система может послать сообщение драйверу диска, запрашивая чтение не
обходимого блока. Драйвер диска использует вызовы ядра для фактического ввода-
вывода и копирования данных между процессами. Эта последовательность (не
сколько упрощенная по сравнению с реальностью) изображена на рис. 3.12, а.
За счет того, что взаимодействие происходит через механизм сообщений, обеспе
чивается стандартный коммуникационный интерфейс между частями системы.
Система, структурированная
Пространство
пользователя
Пространство
ядра
void io_driver() {
initialize О; /* Вызывается только один раз при инициализации */
while (TRUE) {
receive(ANY, &mess); /* Ожидаем запрос */
caller = mess.source; /* Процесс, сделавший запрос */
switch(mess.type) {
case READ: rcode = dev_read(&mess); break;
case WRITE: rcode = dev_write(&mess); break;
/* Тут находится код для выполнения других операций,
таких как OPEN, CLOSE и IOCTL */
default: rcode = ERROR;
}
mess.type = DRIVER_REPLY;
mess.status = rcode; /* Код возврата */
send(caller, &mess); /* Отправляем ответ */
294 Глава 3. Ввод-вывод
момент такие драйверы считаются устаревшими. Они были нужны для поддерж
ки собственных интерфейсов различных производителей дисководов. Современ
ные накопители, как правило, подключаются к IDE-контроллеру, хотя на пор
тативных компьютерах дисководы часто используют интерфейс с шиной USB
(Universal Serial Bus — универсальная последовательная шина). Полная версия
драйвера жесткого диска MINIX 3 включает поддержку CD-ROM, однако в дан
ной книге она не рассматривается.
Конечно, каждому драйверу нужно выполнить некоторые действия для инициа
лизации. Драйвер виртуального диска должен зарезервировать необходимый объ
ем памяти, драйвер жесткого диска — определить параметры устройства и т. д.
Все драйверы дисков вызываются, инициализируются и, завершив инициализа
цию, входят в бесконечный цикл. Этот цикл никогда не завершается, в нем нет
команды выхода. В цикле драйвер получает сообщение, вызывается одна из функ
ций обработки и генерируется ответное сообщение.
Основной цикл, вызываемый каждым из драйверов, компилируется из файлов
каталога драйвера, в том числе drivers / libdri ver /drive г. с, а затем копия
объектного файла driver .о компонуется в исполняемый файл драйвера. Бла
годаря такому подходу каждый драйвер имеет возможность передать в основной
цикл параметр — указатель на таблицу адресов функций, которые драйвер дол
жен косвенно вызывать для выполнения требуемых действий.
Если бы драйверы компилировались в единый исполняемый файл, то потребова
лась бы всего одна копия главного цикла. На самом деле, главный цикл был на
писан для более ранней версии MINIX, в которой драйверы компилировались
вместе. В MINIX 3 акцент сделан на максимальную индивидуализацию компо
нентов операционной системы, однако использование общего исходного кода
разными программами остается хорошим способом повышения надежности.
Создав свободный от ошибок фрагмент кода, вы обеспечиваете им все драйверы,
а если в драйвере обнаружилась ошибка, то с большой вероятностью она присут
ствует и в других драйверах. В результате тестирование общего кода можно про
вести более тщательно.
В файле drivers/libdriver/drvlib.c определен набор функций, потенци
ально полезных для различных дисковых драйверов. Эти функции скомпилирова
ны в объектный файл drvlib. о. Их можно было бы включить в файл driver. о,
однако полный функциональный набор нужен далеко не всем драйверам. Напри
мер, самый простой драйвер memory компилируется исключительно с файлом
driver.о. Драйвер at_wini, напротив, при компиляции использует и файл
driver. о, и файл drvlib. о.
Главный цикл приведен в листинге 3.2. Команды, подобные следующей, являют
ся косвенными вызовами функций:
code = (*entry_points->dev_read)(&mess);
Каждый драйвер вызывает свою функцию dev_read, хотя главный цикл един
для всех драйверов. Но некоторые операции, например close, достаточно про
сты, чтобы для всех устройств можно было использовать одну и ту же функцию.
298 Глава 3. Ввод-вывод
Операция SCATTERED_IO, без сомнений, известна менее всех других. Дело в том,
что трудно достигнуть приемлемой производительности блочного устройства,
если запрашивать данные последовательно, отдельными порциями. Исключение
составляют лишь очень быстрые устройства, такие как виртуальный диск. Опе
рация SCATTERED_IO позволяет системе сделать запрос на чтение или запись
нескольких блоков. В случае операции READ система пытается предугадать,
какие блоки может запросить процесс, и считывает их заранее. В таком запросе
не все затребованные данные обязательны. Каждый из запросов блока данных,
передаваемых драйверу устройства, можно модифицировать, установив бит, со
общающий, что запрос не обязателен. В результате файловая система может соз
дать запрос, который в переводе на человеческий язык звучал бы так: «Было бы
неплохо прочитать все эти данные, но все они вовсе не нужны мне прямо сей
час». Далее устройство само решает, что лучше делать. Например, гибкий диск
может прочитать все данные в пределах одной дорожки, говоря тем самым: «Эти
данные я для тебя прочитаю, а остальные попроси потом, мне слишком долго
переходить на другую дорожку».
Когда необходимо записать данные, не возникает вопроса, обязательно ли запи
сывать каждый отдельный блок — все, что запрошено, обязательно для выполне
ния. Однако система вправе буферизовать несколько запросов на запись в наде
жде на то, что сразу их удастся обработать быстрее, чем по отдельности. При
запросе SCATTERED_IO, будь то для чтения или для записи, список запрошен
ных блоков сортируется, что позволяет считывать данные более эффективно,
чем если бы они считывались в порядке поступления. Кроме того, передача не
скольких блоков за один раз уменьшает количество сообщений, пересылаемых
внутри MINIX 3.
и т. д.). В небольших системах, с одним или двумя дисками, это можно вытер
петь, но на мощных компьютерах с несколькими десятками устройств постоянно
следить за всеми дисками невыносимо. Заметьте, что UNIX работает на компью
терах от IBM PC до рабочих станций и суперкомпьютеров, подобных IBM Blue
Gene/L, a MS-DOS используется только в малых системах.
На рис. 3.13 показано устройство виртуального диска. Диск разбивается на п бло
ков, в зависимости от того, сколько памяти для него выделено. Размер каждого
блока равен размеру блока реального диска. Когда драйвер получает запрос на
чтение или запись блока, он просто вычисляет адрес и производит чтение или
запись по этому адресу, вместо того чтобы обслуживать дискету или жесткий
диск. Обмен данными выполняется системным заданием. Ассемблерная проце
дура ядра phys_copy копирует информацию в пользовательскую программу или
из нее с максимальной скоростью, поддерживаемой аппаратным обеспечением.
Эта команда запустит программу а. out, но все, что она выводит, будет игнориро
ваться. Драйвер виртуального диска считает, что размер этого устройства равен О,
поэтому никаких данных на него не записывается и с него не считывается. При
попытке считывания вы сразу же получите символ конца файла (End of File, EOF).
Освоив назначения файлов каталога /dev/, вы, возможно, обратили внимание
на то, что единственным блочным устройством среди них является /dev/ram,
все остальные устройства — символьные. Драйвер памяти поддерживает еще
одно блочное устройство — /dev/boot. С точки зрения драйвера это блочное
устройство находится в памяти, как и /dev/ram. Тем не менее для его инициа
лизации требуется скопировать в память файл, присоединенный к загрузочному
3.6. Виртуальные диски 309
образу, а не начать с пустого блока, как в случае /dev/ram. Это устройство заре
зервировано на будущее и не используется в текущей версии MINIX 3, которой
посвящена книга.
Наконец, последним устройством, поддерживаемым драйвером памяти, является
символьный файл /dev/zero. Иногда бывает удобно иметь источник нулевых
значений. Запись в /dev/zero эквивалентна записи в /dev/null, однако при
чтении из /dev/zero вы получаете нули в любом нужном количестве — от од
ного символа до целого диска.
На уровне драйвера код, обслуживающий устройства /dev/ram, /dev/mem,
/dev/kmem и /dev/boot, идентичен. Единственное различие между этими че
тырьмя устройствами состоит в том, что они работают с разными областями па
мяти, задаваемыми массивами ram_origin и ram_limit, каждый из которых
индексируется вспомогательным номером устройства. Файловая система управ
ляет устройствами на более высоком уровне. Она различает символьные и блочные
устройства, а следовательно, способна монтировать /dev/ram и /dev/boot,
читать и записывать потоки данных (хотя чтение потока из /dev/null всегда
возвращает символ конца файла).
3.7.2. RAID
Несмотря на то что современные диски значительно быстрее своих предшест
венников, производительность процессоров растет гораздо быстрее, чем дисков.
С течением времени специалистам неоднократно приходила в голову идея о том,
что параллельный дисковый ввод-вывод способен изменить ситуацию. Так по
явился новый класс устройств ввода-вывода, обозначаемый аббревиатурой RAID
(Redundant Array of Independent Disks — избыточный массив независимых дис
ков). Разработчики RAID (университет Беркли) изначально понимали эту аб
бревиатуру иначе: Redundant Array of Inexpensive Disks (избыточный массив
дешевых дисков), позиционируя ее как противоположность другой архитектуре,
SLED (Single Expensive Large Disk — единственный дорогой большой диск). Тем
не менее, когда технология RAID стала пользоваться коммерческой популярно
стью, производители изменили расшифровку аббревиатуры, поскольку дорого
продавать продукт, в названии которого стояло слово «дешевый», было затруд
нительно. Первоначальная идея RAID состояла в установке рядом с компьюте
ром (как правило, большим сервером) коробки, наполненной дисками, замене
дискового контроллера RAID-контроллером, копировании данных в RAID-мас
сив и продолжении обычной работы.
Независимые диски можно совместно использовать целым рядом способов. Из-за
ограничений объема книги мы не можем исчерпывающе описать их все, к тому
же MINIX 3 пока не поддерживает RAID-массивы. Тем не менее при знакомстве
с операционными системами нельзя обойтись без упоминания хотя бы некото
рых из них. RAID позволяет одновременно повысить скорость дискового досту
па и безопасность данных.
Рассмотрим, к примеру, простейший RAID-массив, состоящий из двух дисков.
Когда требуется записать на «диск» несколько секторов данных, RAID-контроллер
316 Глава 3. В вод-вы вод
Начальная Необработанные
Выполнив первый запрос (обращение к цилиндру И), драйвер диска должен вы
брать на обслуживание следующий запрос. Если обслуживать запросы в порядке
поступления, то драйвер должен переместить головку на цилиндр 1, затем на
цилиндр 36 и т. д. В результате выполнение этого алгоритма потребует переме
щения блока головок на 10, 35, 20, 18, 25 и 3 цилиндра, что в сумме составит
111 цилиндров.
Время перемещения головки можно уменьшить, выбирая каждый раз ближай
ший цилиндр. При той же серии запросов (см. рис. 3.14), последовательность их
обработки выглядит как ломаная кривая, представленная в нижней части рисунка.
При такой последовательности выполнения запросов потребуются перемещения
блока головок на 1, 3, 7, 15, 33 и 2 цилиндра, что суммарно равно 61 цилиндр.
Применение данного алгоритма, названного SSF (Shortest Seek First — бли
жайший запрос обслуживается первым), минимизирует суммарные перемещения
головок почти вдвое.
К сожалению, у данного алгоритма есть недостатки. Предположим, во время
обработки запросов, показанных на рис. 3.14, поступили новые запросы. На
пример, после обработки обращения к цилиндру 16 приходит новый запрос
к цилиндру 8. Этот запрос будет иметь приоритет над обращением к цилиндру 1.
Затем, если поступит запрос к цилиндру 13, головка опять начнет перемещаться
к цилиндру 13 для обработки нового запроса, а запрос к цилиндру 1 останется
необработанным. При сильной загрузке диска головка диска будет большую часть
времени находиться где-то в районе средних цилиндров, а запросам к крайним
цилиндрам диска придется ждать, пока нагрузка снизится и обращений к середи
не диска станет меньше. В результате качество обслуживания запросов к цилинд
рам, удаленным от срединной части, может оказаться низким. То есть в конфликт
вступают принцип минимизации времени отклика и принцип справедливости.
В высотных зданиях также приходится иметь дело с данной проблемой планиро
вания обслуживания запросов лифта. Вызовы лифта постоянно поступают с раз
ных этажей. Компьютер, управляющий лифтом, должен следить за последова
тельностью поступления запросов и удовлетворять их либо в порядке подачи
заявок, либо обслуживая первым ближайший запрос.
В большинстве лифтов применяют различные алгоритмы, пытаясь примирить
конфликтующие цели эффективности и справедливости. Обычно лифт продол
жает двигаться в одном направлении до тех пор, пока на этом направлении более
не остается запросов. Затем лифт меняет направление движения. Этот алгоритм,
называющийся элеваторным, требует от программного обеспечения отслежива
ния всего одного бита, хранящего информацию о текущем направлении движе
ния, вверх или вниз. Выполнив очередной запрос, диск или лифт проверяет зна
чение бита. Если он требует движения вверх, кабина лифта (или блок головок)
перемещается в соответствующую сторону к следующему запросу. Если желаю
щих подняться больше нет, состояние бита инвертируется.
Рисунок 3.15 иллюстрирует выполнение элеваторного алгоритма с теми же се
мью запросами, показанными в качестве примера на рис. 3.14. Предполагается,
что изначально бит направления движения указывал вверх. При этом цилиндры
3.7. Реальные диски 319
Начальная
Обработка ошибок
Драйверу виртуального диска не нужно заботиться об оптимизации поиска сек
тора, так как в любой момент можно прочитать или записать любой блок без ка
ких бы то ни было механических движений. Обработка ошибок — еще одна об
ласть, в которой виртуальный диск намного проще. Виртуальный диск работает
всегда, в то время как реальный диск нет. Реальные дисковые накопители под
вержены самым разнообразным ошибкам. Вот самые распространенные из них:
♦ программные ошибки (например, запрос несуществующего сектора);
♦ временная ошибка контрольной суммы (например, вызванная пылью, попав
шей на головку);
♦ постоянная ошибка контрольной суммы (физическое повреждение блока);
♦ ошибка поиска (допустим, головка была отправлена на цилиндр 6, но оказа
лась на цилиндре 7);
♦ ошибка контроллера (например, контроллер отказался принимать команду).
Обрабатывать все эти ошибки наилучшим образом — задача драйвера диска.
Программные ошибки возникают, когда драйвер передает контроллеру команду
на переход к несуществующему цилиндру, на чтение несуществующего сектора,
использование несуществующей головки или на передачу данных в несущест
вующую область памяти. Большинство контроллеров проверяют передаваемые
им параметры и уведомляют о неполадках. Теоретически таких ошибок возни
кать не должно, но что делать, если контроллер все же сообщит об этом? Для
«доморощенной» системы лучше всего было бы вывести на экран текст «свяжи
тесь с разработчиком», чтобы ошибку можно было найти или исправить. Для
коммерческой системы, работающей на многих тысячах компьютеров по всему
миру, такое поведение менее привлекательно. Вероятно, лучшее, что можно сде
лать в данном случае, это завершить текущий запрос с кодом ошибки и надеять
ся, что она не будет случаться слишком часто.
Временные ошибки контрольной суммы вызываются пылинками, которые оста
ются в воздухе между головкой и поверхностью диска. С ними можно бороться,
повторив несколько раз операцию. Если же повторным чтением ошибка не уст
раняется, блок помечается как поврежденный (bad block). Такой блок в дальней
шем не используется.
Один из способов избежать работы с поврежденными блоками требует специаль
ной программы, которая получает на входе список поврежденных блоков и по
3.7. Реальные диски 321
этим данным создает файл, состоящий целиком из таких блоков. Они помечают
ся как занятые, и в результате ни одна программа не будет пытаться обращаться
к ним. В результате ни одна из программ не попытается прочитать файл сбой
ных блоков, и ошибка не возникает.
Не читать поврежденные блоки — это проще сказать, чем сделать. Часто резерв
ные копии дисков создаются путем подорожечного копирования содержимого
диска на ленту или другой диск. Если следовать этой процедуре, избежать повре
жденных блоков невозможно. Пофайловое резервное копирование медленнее, но
может помочь решить проблему, если программа, зная имя файла поврежденных
блоков, не пытается его копировать.
Другая проблема, не решаемая при помощи файла поврежденных блоков, — это
нарушение системной структуры, которая должна занимать фиксированное поло
жение на диске. Практически у любой файловой системы есть структура, поло
жение которой должно быть одним и тем же с целью облегчения ее поиска. Если
файловая система разбита на разделы, то можно заново разбить ее и попробовать
обойти сбойный блок, но если повреждены первые несколько секторов диске
ты или жесткого диска, накопитель становится непригодным к использованию.
«Умные» контроллеры резервируют несколько дорожек, делая их недоступными
пользовательским программам. При форматировании диска контроллер опреде
ляет, какие из секторов содержат ошибки, и автоматически замещает их одним
из запасных. Таблица, отображающая поврежденные блоки на запасные, хранит
ся во внутренней памяти контроллера и на диске. Подмена происходит незамет
но для драйвера, за исключением того, что не исключена потеря эффективности
тщательно отработанного элеваторного алгоритма, если контроллер будет «тайно»
использовать цилиндр 800 вместо запрошенного цилиндра 3. Технология произ
водства магнитных поверхностей исключительно точна, но все равно не идеаль
на. Поэтому механизм скрытия таких дефектов от пользователя имеет большое
значение. Для жестких дисков контроллер отслеживает появление новых сбой
ных блоков и, если ошибка неустранима, заменяет их запасными блоками. Рабо
тая с таким диском, драйвер практически никогда не будет видеть плохие блоки.
Дефектные секторы не являются единственным источником ошибок. Также воз
никают ошибки поиска цилиндра, вызванные механическими проблемами блока
головок. Контроллер следит за положением блока головок. При установке голов
ки на заданный цилиндр он выдает серию импульсов двигателю блока головок,
по одному импульсу на цилиндр. Когда блок головок устанавливается в требуе
мое положение, контроллер считывает истинное значение цилиндра из заголовка
первого попавшегося сектора. Если блок головок оказывается не на той дорожке,
на которой нужно, возникает ошибка поиска и предпринимается некоторое кор
ректирующее действие.
Практически все контроллеры жестких дисков автоматически исправляют ошиб
ки поиска, но большинство контроллеров гибких дисков (включая установленные
на компьютерах IBM PC) просто выставляют бит ошибки и оставляют все осталь
ное драйверу. Драйвер обрабатывает ошибку, выдавая команду recalibrate.
При этом блок головок отодвигается на самую внешнюю дорожку диска до упора.
322 Глава 3. Ввод-вывод
Кэширование дорожек
Время, требующееся на переход к новому цилиндру, обычно много больше, чем
время поворота диска, и всегда больше времени чтения или записи сектора. Дру
гими словами, если драйвер решил перевести куда-либо головку, многое зависит
от того, будет ли читаться только один сектор или вся дорожка. Эффект в осо
бенности заметен, если контроллер позволяет определить, над каким сектором
находится дорожка, чтобы драйвер мог отдать ему команду на чтение следующе
го сектора, тем самым считывая дорожку целиком за один оборот диска. (Обыч
но в среднем на чтение отдельного сектора требуется половина оборота диска
плюс время на чтение одного сектора.)
Некоторые драйверы поддерживают специальный кэш дорожки, невидимый для
программ. Если запрошенный у контроллера сектор находится в этом кэше, об
мениваться данными с диском не нужно. Недостаток такого подхода (помимо
усложнения драйвера и необходимости выделения памяти для буфера) состоит
в том, что передача данных пользовательской программе из кэша должна про
изводиться процессором в программном цикле, а не путем прямого доступа к па
мяти (DMA).
По данной причине многие современные контроллеры поддерживают кэш до
рожки самостоятельно, в своей внутренней памяти. В этом случае кэширование
происходит прозрачно для драйвера и передачу данных можно производить пу
тем прямого доступа к памяти. Если контроллер поддерживает подобную функ
цию, не имеет большого смысла дублировать ее в драйвере. Заметьте, что обеспе
чить чтение одной дорожки целиком могут как контроллер, так и драйвер диска,
но не пользовательская программа, так как для пользовательских программ диск
представляется линейной последовательностью блоков без разбиения на дорожки
и цилиндры. Точная геометрия диска известна лишь его контроллеру.
3.7. Реальные диски 323
но у нас нет другой альтернативы, кроме как верить в то, что заявляет диск.)
Кроме того, эти сведения содержат информацию о том, поддерживает ли диск
линейную адресацию блоков (Linear Block Addressing, LBA). Если данная функция
поддерживается, драйвер вправе игнорировать цилиндры, дорожки и секторы и ад
ресовать секторы диска просто по их абсолютным номерам, что намного проще.
Как уже отмечалось, не исключена ситуация, когда функция init_params
окажется не в состоянии получить информацию о конфигурации диска из BIOS.
Если такое случилось, код в строках 12666—12674 делает попытку сформиро
вать подходящий набор параметров, основанных на тех данных, которые удалось
прочитать с самого диска. Основная идея состоит в том, что номера цилиндра,
дорожки и сектора не должны превышать соответственно 1023, 255 и 63, то есть
учитывается заложенное в структуры данных BIOS ограничение на количество
битов, выделяемых под эти параметры.
Если команда ATA_IDENTIFY возвращает отрицательный результат, это может
просто означать, что вы столкнулись со старой моделью диска, который не под
держивает саму команду. В таком случае все, что мы имеем, — это параметры ло
гической конфигурации, ранее прочитанные функцией init_params. Если они
корректны, то они и записываются в поля структуры wini, в противном случае
сообщается об ошибке, и система отказывается работать с диском.
Наконец, чтобы считать адреса в байтах, в MINIX 3 используется переменная
u32_t. Максимальный объем раздела, с которым умеет работать драйвер, огра
ничен значением в 4 Гбайт. Тем не менее структура device, используемая для
хранения базового адреса и размера раздела и определенная в файле drivers/
libdriver/driver .h (строки 10856-10858), содержит числа типа u64_t. Для
подсчета объема диска применяется операция 64-разрядного умножения (стро
ка 12688). Далее базовый адрес и размер диска вводятся в массив wini и вызы
вается (дважды при необходимости) функция w_specify, передающая обратно
контроллеру его рабочие параметры (строка 12691). Затем выполняются вызовы
ядра. Вызов sys_irqsetpolicy (строка 12699) гарантирует, что по окончании
обслуживания прерывания от контроллера диска прерывания будут разрешены
автоматически. После этого вызов sys_irqenable фактически разрешает пре
рывание.
Функция w_name (строка 12711) возвращает указатель на строку, содержащую
имя устройства: «АТ-DO», «АТ-DI», «AT-D2» или «AT-D3». При необходимо
сти генерации сообщения об ошибке данная функция указывает, какой из дис
ков является ее источником.
Не исключено, что в силу какой-либо причины диск окажется несовместимым
с MINIX 3. Функция w_io_test (строка 12723) проверяет каждый из дисков
при первой попытке получения доступа к нему. Она пытается считать первый
блок диска с более коротким временем ожидания, чем при обычном функциони
ровании. Если попытка не удается, диск помечается как недоступный.
Функция w_specify (строка 12775) в дополнение к передаче параметров кон
троллеру выполняет повторную калибровку устройства (для старых моделей),
передавая команду поиска нулевого цилиндра.
3.7. Реальные диски 331
и не могут исполнять команды чтения и записи регистров. Это означает, что сле
дует изучить вызовы ядра, снимающие данное ограничение.
Регистры, используемые стандартным контроллером жесткого диска, совмести
мым с IBM-АТ, показаны в табл. 3.4.
♦ LBA — для режима адресации по цилиндре, головке и сектору (CHS) — 0, для
режима линейной адресации блоков (LBA) — 1;
+ D — для главного диска (master) — 0, для подчиненного (slave) — 1;
♦ HSn — для режима CHS — выбор головки, для режима LBA биты 24-27 вы
бора блока.
Теперь рассмотрим, как при помощи функции com_out (строка 12947) команда
передается контроллеру. Эта функция вызывается после заполнения структуры
cmd уже изученной нами функцией do_transfer. Прежде чем менять какие-
либо регистры, программа узнает, занят ли контроллер, считывая бит STATUS_
BSY. Здесь важна скорость, и так как контроллер обычно должен быть свободен
или скоро освободится, здесь используется активное ожидание. В строке 12960
вызывается команда w_waitf or, тестирующая значение бита STATUS_BSY. Чтобы
проверить бит в регистре состояния, w_waitfor делает вызов ядра, осуществ
ляющий чтение порта ввода-вывода. Цикл активного ожидания продолжается до
тех пор, пока бит не установится в значение готовности либо не истечет интервал
ожидания. Как только диск готов, цикл быстро прекращается. В качестве возвра
щаемого значения будет указана истина, если контроллер готов сразу, истина, ес
ли контроллер оказывается готов с некоторой задержкой, и ложь, если контрол
лер не приходит в состояние готовности в течение интервала ожидания. Более
подробно интервал ожидания рассматривается при описании функции w_wait f or.
Контроллер способен обслуживать более одного диска, поэтому когда он освобо
ждается, в регистры записывается байт, выбирающий привод, головку и режим
работы (строка 12966), и функция w_waitf or вызывается снова. Иногда приво
ду не удается выполнить команду или правильно вернуть код ошибки; в конце
концов, это — механическое устройство, которое может заесть или просто сломать
ся, и для страховки совершается вызов sys_setalarm ядра, чтобы системное
задание запланировало вызов подпрограммы, разблокирующей драйвер. Следом
за этим команда передается контроллеру, для чего сначала все параметры за
писываются в разные регистры, а затем в регистр команд кладется код самой
команды. Это делает вызов sys_voutb ядра, посылающий вектор пар (значение,
адрес) системному заданию. Системное задание записывает каждое значение
в порт ввода-вывода, задаваемый адресом. Вектор данных для вызова sys_voutb
формируется с помощью макроса pv_set, определенного в файле include/
mimix/devio .h. Обработка начинается, когда в регистр команд записывается
код операции. По завершении генерируется прерывание и отправляется уведом
ление. Если при выполнении команды истечет интервал ожидания, синхронное
уведомление активирует дисковый драйвер.
Следующие несколько функций, которые мы рассмотрим, меньше по объему.
Функция w_need_reset (строка 12999) вызывается при истечении интервала
ожидания прерывания или готовности диска. Все, что делает w_need_reset, —
задает значение переменной state для каждого диска в массиве wini, чтобы
вызвать инициализацию при следующей попытке доступа.
Функция w_do_close (строка 13016) в отношении обычного жесткого диска не
делает почти ничего. Если необходима поддержка CD-ROM, следует включить
в нее дополнительный код.
Функция coinjsiinple отправляет команду контроллеру и немедленно заверша
ется без обмена данными. В данную категорию попадают команды, которые слу
жат для идентификации диска, установки некоторых параметров и повторной
калибровки привода. Пример использования этой функции мы видели в функции
3.7. Реальные диски 335
3.8. Терминалы
В течение десятилетий пользователи взаимодействуют с компьютерами при по
мощи устройств, состоящих из клавиатуры и дисплея. Посредством клавиатуры
пользователь вводит в компьютер данные, а компьютер отображает результаты
на дисплее. Долгое время клавиатура и дисплей объединялись в отдельное уст
ройство, подключаемое к компьютеру проводом. Большие мэйнфреймы, используе
мые в финансовой и туристической отраслях, до сих пор имеют такие терминалы.
340 Глава 3. Ввод-вывод
порт
Рис. 3.17. Отображаемые на память терминалы, доступ через видеопамять
Видеопамять Экран
Адрес ОЗУ
ОхВООАО
ОхВОООО
|<- 80 символов
а б
Рис. 3.18. Область видеопамяти: а — содержимое видеопамяти для монохромного
дисплея IBM; б — соответствующее изображение на экране
Терминалы RS-232
Терминалы RS-232 представляют собой устройства, состоящие из клавиатуры
и экрана, которые взаимодействуют через последовательный интерфейс. Такой
интерфейс позволяет передавать по одному биту за раз (рис. 3.19). Эти термина
лы подключаются через 9- или 25-контактный разъем, в котором один контакт
служит для передачи данных, один для приема и один для заземления. Осталь
ные контакты отвечают за разного рода управляющие функции, и большинство
из них не используется. Чтобы передать символ, терминал RS-232 должен от
правлять по одному биту, предварив передачу стартовым битом и завершив ее
одним или двумя стоповыми битами. Также может быть добавлен бит контроля
четности, который позволяет организовать простейшую проверку правильности
передачи, хотя обычно он требуется только для взаимодействия с мэйнфреймами.
Этот бит может находиться перед стоповыми битами. Стандартно применяются
скорости передачи 14 400 и 56 000 бит/с: первая для факсимильной информа
ции, вторая — для данных. Терминалы RS-232 обычно подключаются к удален
ной системе при помощи модема и телефонной линии.
Компьютер
Интерфейс
Процессор Память RS-232 Передача
Г
“Прием
Г"
Получив символ, драйвер должен начать его обработку. Если клавиатура переда
ет информацию о номере нажатой клавиши, а не о коде символа, который нужен
для прикладных программ, драйверу требуется преобразовать номер в код сим
вола при помощи таблицы. Не все IBM-совместимые клавиатуры имеют одина
ковую нумерацию клавиш, поэтому драйвер, чтобы обеспечить поддержку раз
личных клавиатур, должен иметь разные таблицы перекодировки для разных
клавиатур. Простейший подход — скомпилировать драйвер с таблицей, предна
значенной для преобразования кодов клавиш в кодировку ASCII (American
Standard Code for Information Interchange — американский стандартный код об
мена информацией). К сожалению, такое решение неудовлетворительно для не
англоязычных пользователей. В разных странах приняты разные раскладки кла
виатур, и стандартного набора ASCII-символов недостаточно для большинства
людей, населяющих восточное полушарие. Например, тем, кто разговаривает на
французском, португальском и испанском языках, требуются буквы с надстроч
ными знаками и знаки препинания, которых нет в английском. Чтобы обеспечить
гибкую работу с клавиатурными раскладками для различных языков, во многих
операционных системах имеется возможность загружать различные кодовые
страницы. Они позволяют выбирать (при загрузке или позже), как клавиатур
ные коды должны преобразовываться в коды символов.
Если терминал работает в каноническом режиме (режиме с обработкой), введен
ные символы должны храниться в буфере до тех пор, пока не будет завершена
вся строка, поскольку пользователь может решить удалить ее часть. Даже если
переключить терминал в неканонический режим, может оказаться, что програм
ма еще не запрашивала входные данные, поэтому введенные символы все равно
должны буферизироваться, чтобы позволить пользователю производить упреж
дающий ввод. (Разработчиков систем, не позволяющих пользователям вводить
символы с клавиатуры без возможности исправления, следует обмазывать дег
тем и вываливать в перьях, ибо заставить их пользоваться собственной системой
было бы слишком жестоким наказанием.)
Для буферизации символов обычно применяются два метода. В первом случае
в драйвере содержится центральный пул буферов, в каждом из которых хра
нится около 10 символов. С каждым терминалом связана структура данных, со
держащая, среди прочего, указатель на цепочку буферов, в которых находятся
символы, введенные с данного терминала. Чем больше символов введено, тем
больше выделяется буферов, соединенных в цепь. Когда символ передается
пользовательской программе, буферы удаляются и память возвращается цен
тральному пулу.
Другой подход заключается в том, что буферизация производится прямо в струк
туре данных терминала, без центрального пула буферов. Поскольку пользовате
ли часто печатают команду, обработка которой требует некоторого времени (на
пример, на перекомпиляцию и сборку большой программы), а затем печатают
еще несколько строк, буфер драйвера должен вмещать не меньше 200 символов
для каждого терминала. В большой системе разделения времени с сотней терми
налов выделение по 20 Кбайт на буфер ввода с каждой клавиатуры кажется
3.8. Терминалы 347
Буферная область
для терминала 0
Буферная область
для терминала 1
а б
Рис, 3-20- Варианты буферизации ввода: а — центральный пул буферов;
б — выделенный буфер для каждого терминала
старые соглашения, могут переопределить символ KILL как @, но тогда при набо
ре адреса электронной почты им придется вводить символ нажимая сочетание
клавиш Ctrl+V, Сам символ LNEXT может быть введен, если дважды нажать
клавиши Ctrl+V. Встретив символ LNEXT, драйвер установит флаг, означающий,
что следующий символ не следует подвергать специальной обработке. Сам сим
вол LNEXT не помещается в очередь символов.
Чтобы приостановить и продолжить вывод на экран, также предоставляются спе
циальные управляющие коды. В MINIX 3 это символы STOP (Ctrl+S) и START
(Ctrl+Q). Они не хранятся в буфере, но используются для установки и сброса
флага в структуре данных терминала. При каждой операции вывода на экран
проверяется значение этого флага. Если флаг установлен, вывод не производит
ся. Эхопечать при этом, как правило, также подавляется.
Часто возникает необходимость прервать выполнение отлаживаемой програм
мы. Для этой цели могут использоваться символы INTR (Ctrl+C) и QUIT (Ctrl+\).
В MINIX 3 сочетание Ctrl+C посылает сигнал прерывания SIGINT всем процес
сам, запущенным с этого терминала. Реализация может быть непростой. Наи
более сложной является передача информации от драйвера в ту часть системы,
которая занимается обработкой сигналов, поскольку она не ожидает получения
подобной информации. Результат нажатия клавиш Ctrl+\ аналогичен нажатию
клавиш Ctrl+C, с той разницей, что процессам посылается сигнал SIGQUIT, вы
зывающий прекращение работы процесса с сохранением дампа ядра, если этот
сигнал специально не перехватывается процессом или не игнорируется.
При нажатии любого из этих сочетаний клавиш драйвер должен вывести эхо
в виде символов перевода строки и возврата каретки, а также очистить свой бу
фер с накопленными символами, чтобы позволить начать новый ввод. Во многих
UNIX-системах символ INTR по умолчанию генерируется клавишей Del. Посколь
ку программы зачастую используют Del наравне с клавишей забоя при редакти
ровании, в настоящее время предпочтение отдается комбинации Ctrl+C.
Специальный символ EOF (Ctrl+D) в MINIX 3 удовлетворяет активный запрос на
чтение и передает все содержимое буфера, даже если он пуст. Нажатие клавиш
Ctrl+D в начале строки приводит к тому, что программа считывает 0 байт. При
считывании файла это обычно воспринимается и обрабатывается как конец файла.
Некоторые драйверы терминала предоставляют возможность более сложного ре
дактирования строки, чем было описано здесь. Они имеют специальные управ
ляющие символы, позволяющие удалять целиком слова, перемещать курсор впе
ред и назад по символам и по словам, вставлять текст в середину уже набранной
строки и т. д. Добавление подобных функций к драйверу значительно увели
чивает его. Кроме того, эти функции чаще всего оказываются неиспользуемыми
экранными редакторами, предпочитающими работать с драйверами клавиатуры
в режиме без обработки.
Стандарт POSIX требует того, чтобы в стандартной библиотеке были доступны
несколько функций, позволяющих программам управлять параметрами терми
3.8. Терминалы 351
Таблица 3,7, Параметры MIN и TIME определяют, как выполняется чтение в неканоническом
режиме. N — число запрошенных байтов
TIME = 0 TIME > 0
MIN = 0 Вызов завершается немедленно, Сразу же запускается таймер. Возвращается
возвращая имеющееся число первый полученный байт или ни одного,
байтов, от 0 до N если истекло время
MIN > 0 Вызов возвращает от MIN После первого байта запускается
до N байт. Возможна межбайтовый таймер. Возвращается
бесконечная блокировка N байт, если они уложились во временной
интервал, но не меньше 1 байта. Возможна
бесконечная блокировка
Каждая из структур tty в массиве tty_table отслеживает как ввод, так и вы
вод. При вводе она поддерживает очередь символов, которые были введены
пользователем, но еще не считаны, информацию о запросе на чтение символов
и об интервалах таймера, чтобы драйвер не блокировался, если не нажато ни од
ного символа. При выводе она хранит параметры запросов на вывод, которые
еще не были выполнены. Также есть другие поля с различной общей информа
цией, например с описанной ранее структурой termios, от которой зависят
многие характеристики ввода и вывода. Кроме того, в структуре tty есть указа
тель, который ссылается на информацию, необходимую для конкретного класса
устройств, но не требуемую для всех терминалов. Так, завязанной на аппаратное
обеспечение части кода драйвера терминала требуется информация о текущем
положении вывода в видеопамяти, но при работе с линией RS-232 эта информа
ция не нужна. Дополнительно для каждого типа устройств имеются собственные
структуры данных, содержащие информацию о расположении буферов, в кото
рые помещают свои данные обработчики прерываний. Медленным устройствам,
таким как клавиатуры, не нужны такие большие буферы, как быстрым.
Терминальный ввод
Чтобы лучше понять, как работает драйвер терминала, сначала рассмотрим, как
напечатанный пользователем символ прокладывает себе путь к ожидающей его
программе. Хотя этот раздел является обзорным, мы дадим читателю ссылки на
3.8. Терминалы 357
Получив достаточно символов, драйвер терминала выполняет вызов ядра (S), за
прашивающий у системного задания копирование данных по адресу, затребован
ному оболочкой. Эта операция также не является передачей сообщения, поэтому
обозначена на рис. 3.21 многоточием (9). На рисунке изображено несколько
линий с цифрой 9, поскольку до того, как пользовательский запрос будет полно
стью выполнен, данные могут быть переданы несколько раз. Когда операция
полностью завершится, драйвер терминала отправляет файловой системе со
общение о том, что работа выполнена (10), а файловая система в результате от
правляет сообщение оболочке и выводит ее из состояния блокировки (11).
Определение количества обрабатываемых символов зависит от режима термина
ла. В каноническом режиме запрос считается выполненным, когда поступает
символ перевода строки, конца строки или конца файла. Также длина строки не
может превышать величину входной очереди, чтобы входные данные могли пра
вильно обрабатываться. В неканоническом режиме может запрашиваться гораз
до большее количество символов, и функции in_process может потребоваться
передавать данные несколько раз перед тем, как файловой системе будет возвра
щено сообщение, означающее завершение работы.
Обратите внимание на то, что системное задание копирует данные напрямую из
своего адресного пространства в адресное пространство оболочки. Данные не пе
редаются через файловую систему. При блочных операциях ввода-вывода ин
формация сначала поступает файловой системе, чтобы та могла поддерживать
кэш недавно запрошенных блоков. Если запрошенный блок оказывается в кэше,
файловая система может самостоятельно выполнить пользовательский запрос
без реального обращения к диску.
При работе с клавиатурой кэширование не имеет смысла. Кроме того, запрос фай
ловой системы к драйверу диска всегда может быть обслужен максимум за не
сколько сотен миллисекунд, поэтому нет большой проблемы в том, что файловой
системе приходится немного подождать. Операции с клавиатурой могут длиться
часами или вообще могут не завершаться (в каноническом режиме драйвер ждет
ввода полной строки, а в неканоническом ждет достаточно длинной строки, в за
висимости от параметров MIN и TIME). Поэтому файловая система не должна
блокироваться, ожидая выполнения запросов терминального ввода-вывода.
Вполне вероятно, что пользователь введет какой-либо текст заранее, и символы
благодаря предыдущим прерываниям и событию 4 окажутся доступными до то
го, как они будут запрошены. В этом случае все события 1,2^5-11 происходят
сразу же после запроса, а события 3 вообще не происходит.
Читатели, знакомые с ранними версиями операционной системы MINIX, возмож
но, помнят, что в них драйвер терминала и все прочие драйверы были объединены
с ядром. У каждого драйвера был собственный обработчик прерываний в про
странстве ядра. Обработчик прерываний драйвера клавиатуры осуществлял бу
феризацию определенного количества кодов опроса и выполнял предваритель
ную обработку (большую часть кодов, полученных при отпускании клавиши,
можно отбросить, поскольку их буферизация необходима только при нажатии
360 Глава 3. Ввод-вывод
клавиатурного ввода (рис. 3.22). В первом случае, когда драйвер терминала полу
чает сообщение, запрашивающее вводимые с клавиатуры символы, главная про
цедура, tty_task (строка 13740), вызывает процедуру do_read (строка 13953),
которая и выполняет запрос. Если имеющейся в буфере информации недостаточно
для выполнения запроса, процедура сохраняет параметры запроса в tty_table.
Рис. 3.22. Обработка ввода драйвером терминала. Левая часть дерева соответствует
обработке запроса на чтение символов, а правая — передаче сообщения от клавиатуры
драйверу раньше, чем пользователь затребовал ввод
После этого, чтобы получить уже введенные данные, вызывается функция in_
transfer (строка 14416), а затем — функция handle_events (строка 14358), ко
торая, в свою очередь, вызывает функцию kb_read через указатель *tp->tty_
devread (строка 15360) и еще раз функцию in_transfer, чтобы получить из
входного потока еще несколько символов. Функция kb_read несколько раз вы
зывает другие подпрограммы, не показанные на рис. 3.22. В результате пользова
телю копируются все доступные данные. Если доступных данных нет, ничего не
копируется. Когда вызов in_transf er или handle_events полностью выпол
няет запрос, файловой системе отправляется сообщение, чтобы она могла раз
блокировать запросивший данные процесс. Если чтение не завершено (не введе
но ни одного символа или введено недостаточное количество), функция do_
report информирует об этом файловую систему, чтобы та могла либо заблоки
ровать вызвавший процесс, либо, если было запрошено неблокирующее чтение,
отменить запрос.
На правой части рис. 3.22 воспроизведены все события, возникающие, когда
драйвер терминала «просыпается» вследствие прерывания от клавиатуры. Когда
напечатан символ, «обработчик» прерываний kbd_interrupt (строка 15335) вы
зывает функцию scan_keyboard, которая обращается к системному заданию за
выполнением ввода-вывода (мы заключили слово «обработчик» в кавычки пото
му, что функция kbd_interrupt фактически обработчиком не является. Она вы
зывается, когда tty_task получает сообщение от функции generic_handler
362 Глава 3. Ввод-вывод
Терминальный вывод
Вообще говоря, консольный вывод проще ввода, так как здесь действиями управ
ляет операционная система, и не нужно бояться, что события произойдут в не
подходящее время. Более того, процесс еще больше упрощается за счет того, что
в MINIX 3 консолью является отображаемый на память экран. Прерывания
не нужны, вывод производится путем копирования данных из одной области
памяти в другую. Однако в этом случае все операции по управлению экраном,
включая обработку управляющих последовательностей, ложатся на программ
ное обеспечение драйвера. Как и при изучении ввода в предыдущем разделе,
мы проследим, какие действия происходят при выводе символа на консольный
дисплей. В этом примере предполагается, что вывод производится на актив
ный дисплей. Небольшое усложнение, вносимое виртуальными консолями, мы
обсудим позже.
Обычно, когда процесс хочет что-нибудь напечатать, он вызывает функцию
print f. Эта функция делает системный вызов write, который отправляет фай
ловой системе сообщение с указателем на выводимые символы (но не сами
символы). Затем файловая система посылает сообщение драйверу терминала,
а тот извлекает указанные ему символы и копирует их в видеопамять. Основ
ные подпрограммы, из которых складывается терминальный вывод, показаны
на рис. 3.23.
3.8. Терминалы 363
программа genmap. При компиляции в genmap включается код keymap. src для
определенной раскладки. Обычно программа genmap запускается сразу же после
компиляции и записывает уплотненную версию в файл, после чего исполняемый
файл программы удаляется. Команда loadkeys загружает указанную карту кла
виш, выполняет декомпрессию и делает системный вызов ioctl, передавая рас
кладку в память ядра. MINIX 3 может автоматически вызывать команду loadkeys
при старте. Кроме того, ее в любой момент может вызвать пользователь.
Исходный код раскладки описывает большой инициализированный массив.
С целью экономии места мы исключили файл карты клавиш из файлов, пред
ставленных на сопровождающем книгу компакт-диске. В табл. 3.11 показано
содержимое нескольких строк файла src/kernel/keymaps/us-std.src. На
клавиатурах IBM PC нет клавиши, генерирующей код опроса 0. Коду 1 соответ
ствует клавиша Esc. Из первой записи в таблице видно, что возвращаемый код
для этой клавиши не зависит от состояния модификатора Shift или Ctrl, но если
удерживается клавиша Alt, при нажатии клавиши Esc генерируется другой код.
Загружаемые шрифты
В ранних персональных компьютерах шаблоны, по которым генерировались изо
бражения символов на экране, хранились только в ПЗУ. Теперь у контроллеров,
которыми оснащаются современные системы, образы символов можно загружать
в оперативную память. В MINIX 3 эта возможность поддерживается с помощью
системного вызова ioctl следующего вида:
ioctl(0, TIOCFON, font)
MINIX 3 поддерживает видеорежим 80 столбцов на 25 строк, и файлы со шриф
тами содержат 4096 байт. Каждый байт такого файла управляет отрезком из
восьми пикселов, при этом подсвеченному пикселу соответствует 1, погашен
ному — 0. Для генерации символа необходимо 16 таких отрезков. Тем не ме
нее контроллер дисплея для хранения изображения каждого символа отводит
32 байта, чтобы работать при более высоком разрешении, не поддерживаемом
сейчас MINIX 3. Для того чтобы преобразовать такой файл в 8192-байтовую
структуру font, адрес которой передается в системный вызов ioctl, предна
значена команда load font. Как и карты клавиш, шрифты могут загружаться
при запуске системы или же позднее, в любой момент нормальной работы. Одна
ко загружать шрифт не обязательно, у каждого видеоадаптера в ПЗУ имеется
встроенный шрифт, доступный по умолчанию. Встраивать шрифт в саму систе
му нет необходимости, поэтому единственное, что присутствует в ядре для под
держки загружаемых шрифтов, — это код, выполняющий операцию TIOCSFON
системного вызова ioctl.
Точкой входа для драйвера терминала является функция tty_task (строка 13740).
Перед тем как начать свой главный цикл, она вызывает функцию tty_init
(строка 13752). Информация о главном компьютере, необходимая для инициа
лизации клавиатуры и консоли, предоставляется вызовом sys_getmachine
ядра, а затем выполняется сама инициализация при помощи подпрограммы kb_
init_once. Такое имя было выбрано, чтобы отличить эту подпрограмму от дру
гой подпрограммы инициализации, вызываемой позднее для каждой виртуаль
ной консоли. Наконец, на вывод посылается один символ 0, чтобы проверить вывод
и исключить неинициализированные устройства раньше, чем будут предприня
ты первые попытки их использования. В коде вызывается функция print f, но
она отличается от одноименной функции, вызываемой обычными программами.
Она вызывает локальную функцию putk драйвера консоли.
Главный цикл (строки 13764-13876) почти аналогичен главному циклу любого
другого драйвера. Принимается сообщение, при помощи инструкции switch
для каждого типа сообщения вызывается соответствующая функция обработки,
после чего генерируется ответное сообщение. Однако есть и отличия. Первое со
стоит в том, что с момента последнего прерывания могут накопиться считанные
или выведенные на запись символы. Поэтому, прежде чем пытаться получить со
общение, главный цикл проверяет флаги tp->tty_events для всех термина
лов. Если необходимо позаботиться о незавершенных делах, вызывается функ
ция handle_events, и только если не найдено ни одного требующего внимания
терминала, делается вызов receive.
Диаграмма с типами сообщений, представленная в комментариях в начале файла
tty. с, содержит лишь наиболее распространенные типы. В ней отсутствуют мно
гие типы, требующие особой обработки драйвером терминала. Те же, которые на
ней изображены, не являются специфичными для какого-либо конкретного уст
ройства. Главный цикл tty_task проверяет общие сообщения и обрабатывает
их раньше, чем специфичные. Первая проверка относится к типу SYN_ALARM,
и если сообщение относится к нему, выполняется вызов expire_timers, запус
кающий подпрограмму сторожевого таймера. Затем следует команда continue.
Фактически, все следующие варианты, которые мы рассмотрим, расположены
после этой команды. Скоро мы скажем об этом более подробно.
Следующий тип, соответствие которому проверяется для сообщения, — HARD_
INT. Сообщения такого типа чаще всего обусловлены нажатием или отпускани
ем клавиши на локальной клавиатуре. Сообщения HARD_INT также генерируются
при получении байтов по последовательному порту в случае, если он включен.
В конфигурации, описываемой в книге, последовательные порты не используют
ся, однако мы сохранили в файле условный код, чтобы продемонстрировать, как
система функционировала бы при поддержке последовательных портов. Битовое
поле сообщения позволяет определить источник прерывания.
Затем проверяется тип SYS_SIG. Системные процессы (драйверы и серверы),
как правило, блокируются и ожидают сообщений. Обычные сигналы способны
получать лишь активные процессы, поэтому стандартный метод использова
ния сигналов UNIX неприменим к системным процессам. Для передачи сигнала
3.8. Терминалы 375
0 V D N с с с с 7 6 5 4 3 2 1 0
Четыре бита используются для подсчета символов, чтобы знать, сколько места
осталось для эхопечати. Условная инструкция в строке 14435 проверяет, уста
новлен ли признак конца файла (D на рисунке). Эта проверка делается в начале
потому, что символ конца файла не должен ни передаваться программе, ни
использоваться при подсчете символов. При передаче каждого символа у него
маскируется 8 старших бит, и в локальный буфер записывается только ASCII-
значение (строка 14437).
Существуют несколько способов сигнализировать об окончании ввода, но от спе
цифической для устройства функции ввода ожидается, что она распознает, когда
вводимый символ является переносом строки, концом файла (Ctrl+D) или одним
из подобных символов, и соответствующим образом пометит их. Функции in_
transfer остается только проверить эту отметку, бит IN_EOT (N на рис. 3.24)
в строке 14454. Если символ опознан, значение tp->tty_eotct уменьшается
на единицу. В неканоническом режиме подобным образом подсчитываются все
символы, и все они помечаются битом IN_EOT, поэтому tp->tty_eotct содер
жит число символов, не удаленных из очереди. Единственное различие в работе
главного цикла процедуры в двух разных режимах работы терминала определя
ется строкой 14457. Там обнуляется переменная tp->tty_eotct, когда полу
чен символ, помеченный как разрыв строки, но делается это лишь тогда, когда
действует канонический режим. Таким образом, когда управление вновь переда
ется в начало цикла, тот корректно завершается после символа разрыва строки.
Но сказанное верно только для канонического режима — в неканоническом раз
рывы строк игнорируются.
384 Глава 3. В вод-вы вод
Когда цикл завершается, локальный буфер символов для передачи обычно час
тично полон (строки 14461-14468). В том случае, если значение tp->tty_
inleft достигло нуля, генерируется и отправляется ответное сообщение. В кано
ническом режиме это делается всегда, а в неканоническом число символов в бу
фере сравнивается с минимальным передаваемым количеством, и если символов
недостаточно, ответное сообщение не отправляется. Если у вас достаточно хорошая
память, чтобы запомнить вызовы in_transf er (в do_read и handle_events),
вы, наверно, будете удивлены. Там код, следующий за вызовом, отправлял ответ
ное сообщение, когда функция in_transf er передавала больше символов, чем
указано в tp->tty_min. И здесь именно этот случай. Причина, по которой от
ветное сообщение не отправляется непосредственно из in_transf er, раскрыва
ется чуть позже при обсуждении следующей функции, вызывающей функцию
in_transf er при других обстоятельствах.
Эта следующая функция носит имя in_process (строка 14486). Она вызывается
из аппаратно-зависимого кода для выполнения общих действий, необходимых
всегда. Ее аргументами являются указатель на структуру tty для исходного
устройства, указатель на массив 8-разрядных символов, подлежащих обработке,
и счетчик. Новое значение счетчика возвращается в вызвавшую программу.
Функция in_process весьма велика, но ее работа не сложна. Она добавляет
16-разрядные символы во входную очередь, откуда их в дальнейшем извлекает
функция in_transfer.
Функцией in_transf er символы разделяются на несколько категорий.
1. Обычные символы, расширенные до 16 бит, которые добавляются в очередь.
2. Символы, управляющие дальнейшей обработкой, модифицируют флаги, но
сами в очередь не помещаются.
3. Символы для эхопечати применяются немедленно, без занесения в очередь.
4. Символы, имеющие специальное значение, заносятся в очередь с установлен
ными в старшем байте специальными битами, например, такими как бит EOT.
Сначала рассмотрим совершенно стандартную ситуацию, когда обычный символ,
такой как х (ASCII-код 0x78), вводится в середине короткой строки без воздей
ствия ESC-кодов на терминале, «настроенном» по умолчанию. Полученный
от устройства ввода, этот символ занимает биты от 0 до 7 (см. рис. 3.24). В строке
14504 старший значащий бит сбрасывается в ноль, если установлен бит ISTRIP.
Но по умолчанию MINIX 3 не обнуляет старший бит, позволяя вводить 8-раз-
рядные символы. В любом случае символ х это не затронет. Расширенная обра
ботка ввода по умолчанию в MINIX 3 не разрешена, поэтому проверка бита
IEXTERN в переменной tp->tty_termios . c_lf Lags (строка 14507) дает поло
жительный результат, но для всех последующих проверок результат отрицателен
вследствие установленных нами условий: не действует ESC-код (строка 14510),
сам символ не является ESC-кодом (строка 14517) и это — не символ REPRINT
(строка 14524).
Проверки в следующих нескольких строках обнаруживают, что символ не явля
ется специальным символом _POSIX_VDISABLE, ни CR, ни NL. Наконец, одна
3.8. Терминалы 385
функции очередь все еще активна, выполняется вызов ядра sys_setalarm, за
прашивающий очередное сообщение SYN_ALARM. Наконец, функция settimer
(строка 15089) устанавливает таймеры, определяющие срок возврата из вызова
read в неканоническом режиме. Она вызывается с двумя параметрами: tty_ptr,
указателем на структуру tty, и enable, целым числом, обозначающим истину
или ложь. Библиотечные функции tmrs_settimer и tmrs_clrtimer исполь
зуются для включения и отключения таймера в соответствии со значением аргу
мента enable. Если таймер включен, то в качестве сторожевой функции всегда
используется описанная ранее функция tty_timed_out.
Описание функции tty_devnop (строка 15125) длиннее, чем ее код, поскольку
последний попросту отсутствует. Это — пустая функция, косвенно вызываемая
в случае, если обслуживание устройству не требуется. Мы видели, что в tty_
init многие указатели функций по умолчанию указывают на tty_devnop до
тех пор, пока не выполнена процедура инициализации устройства.
Последняя функция в файле tty. с заслуживает особого внимания — это функ
ция select. Она представляет собой системный вызов, используемый, когда
одному процессу необходимо асинхронно обслуживать несколько устройств
ввода-вывода. Классическим примером является программа связи, работающая
с локальной клавиатурой и удаленной системой, возможно, соединенными при
помощи модема. Вызов select дает возможность открыть несколько файлов
устройств и наблюдать за их считыванием или записью без блокирования. В от
сутствие select для двусторонней связи необходимо использовать два отдель
ных процесса, один из которых является главным и обеспечивает связь в одном
направлении, а другой является подчиненным и обеспечивает связь в другом на
правлении. Вызов select является примером удобной возможности, значитель
но усложняющей систему. Одной из целей разработки MINIX 3 является про
стота, достаточная для освоения операционной системы с разумными усилиями
за разумное время. По этой причине мы несколько ограничимся в повествовании
и не станем рассматривать здесь функцию do_select (строка 15135), а также
поддерживающие подпрограммы select_try (строка 14313) и select_retry
(строка 14348).
никто не может заявить, что после сбоя, когда ожидается команда на перезагруз
ку, имеет смысл применять более эффективные методики. Внутри цикла редко
используемая неблокирующая операция приема nb_receive служит для прие
ма сообщений и проверки ввода с клавиатуры одного из вариантов, предложен
ных в сообщении:
Hit ESC to reboot, DEL to shutdown, F-keys for debug dumps
В следующем разделе мы рассмотрим код функций do_newkmess и do_
diagnostics.
числовых
аргументов
Рис. 3.26. Конечный автомат для обработки ESC-последовательностей
Резюме
Вводом-выводом часто пренебрегают, хотя он заслуживает более серьезного от
ношения. Значительная доля кода любой операционной системы связана с вво
дом-выводом. Тем не менее драйверы устройств ввода-вывода часто становятся
причиной проблем. Как правило, драйверы создают программисты, работающие
в компаниях-производителях устройств. Традиционные операционные системы
предоставляют драйверам доступ к критически важным ресурсам, таким как пре
рывания, порты ввода-вывода и память других процессов. В MINIX 3 драйверы
являются независимыми процессами с ограниченными привилегиями. По этой
причине ошибка в одном драйвере не приводит к краху всей системы.
Мы начали с рассмотрения аппаратного обеспечения ввода-вывода и связи уст
ройств ввода-вывода с контроллерами. Именно такую связь создает программ
ное обеспечение. Затем мы рассмотрели четыре уровня программного обеспече
ния ввода-вывода: обработчики прерываний, драйверы устройств, независимое
от устройств программное обеспечение и библиотеки плюс спулеры, работаю
щие в пользовательском пространстве.
Далее мы изучили проблему взаимной блокировки и инструментарий для борь
бы с ней, Взаимная блокировка возникает, когда имеется группа процессов,
получивших монопольный доступ к некоторым ресурсам, и каждому процессу
в группе необходим помимо этого другой ресурс, принадлежащий другому процес
су. В таком случае все процессы блокируются и не могут выполняться. Взаимную
блокировку можно исключить, если система построена в расчете на упреждение
подобной ситуации. Например, можно разрешить каждому процессу удерживать
не более одного ресурса в каждый момент времени. Другой способ избежать бло
кировки — проверять каждый запрос, определяя, ведет ли он к опасной ситуации
(в которой возможно возникновение блокировки) и отменять или откладывать
опасные запросы.
В MINIX 3 драйверы устройств реализованы в виде процессов, встроенных в яд
ро. Мы рассмотрели драйверы виртуального диска, жесткого диска и терминала.
У каждого из этих драйверов есть главный цикл, в котором принимаются и об
рабатываются запросы, в конечном итоге формируется и отправляется ответное
сообщение о результатах. Исходный код главных циклов и общих функций драй
веров помещен в единую библиотеку драйверов, хотя каждый драйвер компили
руется и связывается с собственными копиями библиотечных процедур. Адресные
пространства драйверов индивидуальны. Множество различных терминалов, ис
пользующих системную консоль, последовательные линии и сетевые соединения,
поддерживаются одним и тем же процессом драйвера терминала.
408 Глава 3. Ввод-вывод
Вопросы и задания
1. Устройство для чтения DVD со скоростью 1х способно предоставлять 1,32 Мбайт
данных в секунду. Какова максимальная скорость устройства, которое мож
но подключить к компьютеру с помощью интерфейса USB 2.0 без потери
данных?
2. Многие диски содержат коды исправления ошибок (ЕСС) в конце каждого
сектора. Какие действия следует предпринять, если сам код ошибочен? Какой
программный или аппаратный компонент должен это делать?
3. Что такое ввод-вывод, отображаемый на память? Для чего он используется?
4. Объясните, что такое прямой доступ к памяти (DMA) и для чего он исполь
зуется.
5. Хотя DMA не использует процессор, максимальная скорость передачи дан
ных остается ограниченной. Предположим, вы считываете блок с диска. На
зовите три фактора, ограничивающие скорость его передачи.
6. Музыка с качеством, соответствующим компакт-диску, должна иметь частоту
дискретизации сигнала 44 100 Гц. Предположим, что таймер генерирует пре
рывания с такой частотой, а обработка прерывания занимает 1 мс на про
цессоре со скоростью 1 ГГц. Какова наименьшая частота таймера, обеспе
чивающая сохранение всех данных? Положим, что число команд обработки
прерывания постоянно, следовательно, удвоение частоты таймера удваивает
время обработки прерывания.
7. Альтернативой прерываниям является опрос. Существуют ли ситуации, в ко
торых опрос предпочтительней?
Вопросы и задания 409
30. В UNIX каждый процесс состоит из двух частей, работающих в адресном поль
зовательском пространстве и в пространстве ядра. Является ли часть в про
странстве ядра подпрограммой или сопрограммой?
31. На некотором компьютере обработчик прерываний от таймера выполняет
свои действия за 2 мс (включая накладные расходы по переключению про
цессов). Прерывания от таймера поступают с частотой 60 Гц. Какая часть вре
мени работы центрального процессора расходуется на таймер?
32. В тексте описано два варианта применения сторожевых таймеров: ожидание
запуска двигателя дисковода и выполнение возврата каретки на печатных тер
миналах. Приведите еще один пример.
33. Почему терминалы, использующие интерфейс RS-232, управляются прерыва
ниями, а терминалы с отображением на память — нет?
34. Рассмотрим работу терминала. Драйвер посылает один символ, после чего
блокируется. За передачей символа в линию следует прерывание, затем драй
вер разблокируется и посылает следующий символ и т. д. Какую часть време
ни центрального процессора занимает управление модемом, если обработка
прерывания, вывод одного символа и каждая блокировка требуют 4 мс? Бу
дет ли этот метод работать с линиями 110 бод? А что насчет линий 4800 бод?
35. Вообразите терминал, содержащий 1200 х 800 пикселов. Для прокрутки окна
центральный процессор (или контроллер) должен переместить все строки
текста вверх, копируя их биты из одной части видеопамяти в другую. Допус
тим, в окне 66 строк по 80 символов в строке (всего 5280 символов), а каждый
символ имеет 8 пикселов в ширину и 16 пикселов в высоту. Сколько времени
займет прокрутка всего окна, если для копирования одного байта требуется
500 нс? Если все строки имеют по 80 символов в длину, чему будет равна эк
вивалентная скорость терминала в бодах? Помещение одного символа на
экран занимает 50 мкс. Подсчитайте скорость, если терминал цветной и имеет
4 бит/пиксел (в этом случае помещение символа на экран занимает 200 мкс).
36. Для чего в операционных системах нужны ESC-последовательности, напри
мер Ctrl+V в MINIX?
37. Получив символ SIGINT (Ctrl+C), драйвер экрана MINIX очищает всю оче
редь на вывод для этого экрана. Почему?
38. У многих терминалов, использующих интерфейс RS-232, есть ESC-последо
вательности для удаления текущей строки и перемещения всех нижних строк
на одну строку вверх. Как, по-вашему, реализована эта операция внутри тер
минала?
39. На оригинальном компьютере IBM PC с цветным дисплеем запись в видеопа
мять в любое время, кроме того интервала, когда электронный луч совершал
вертикальный обратный ход, вызывала появление уродливых пятен по всему
экрану. На экран выводятся 25 строк по 80 символов, каждый из которых по
мещается в квадрат 8x8 пикселов. Каждый ряд из 640 пикселов рисуется за
один горизонтальный проход луча, что занимает 63,6 мкс, включая горизон
тальное обратное движение луча. Экран перерисовывается 60 раз в секунду.
Вопросы и задания 413
OxFFF Драйверы
Операционная устройств
система в ПЗУ в ПЗУ
Программа
пользователя
Программа
пользователя
Программа
пользователя
Операционная Операционная
система в ПЗУ система в ПЗУ
а б в
Рис. 4.1. Три простейшие модели организации памяти при наличии операционной системы
и одного пользовательского процесса. Существуют и другие варианты
Несколько
входных очередей
800 К
LHJ— Раздел 4
700 К
Раздел 3
400 К
Раздел 2
200 К
ЕЮ£>- Раздел 1
100 К
Операционная
система
0
а
Рис. 4.2. Многозадачность с фиксированными разделами памяти: а — фиксированные
разделы с отдельными входными очередями для каждого раздела; б — фиксированные
разделы с одной общей входной очередью
4.2. Подкачка
В случае пакетных систем память с фиксированными разделами действует про
сто и эффективно. Каждое задание после ожидания в очереди загружается в раз
дел памяти и остается там до своего завершения. До тех пор пока в памяти мо
жет храниться достаточное количество заданий для обеспечения постоянной
занятости центрального процессора, нет причин что-либо усложнять.
Но совершенно другая ситуация имеет место в системах разделения времени и пер
сональных компьютерах, ориентированных на работу с графикой. Оперативной
памяти иногда оказывается недостаточно для того, чтобы вместить все текущие
420 Глава 4. Управление памятью
Рис. 4.3. Перераспределение памяти по мере того, как процессы поступают в память
и покидают ее. Заштрихованы неиспользуемые области памяти
в Действительно
используется
А Действительно
используется
Операционная
система
a
Рис, 4,4, Распределение памяти в случае расширения процессов: а — предоставление
пространства для роста области данных; б — предоставление пространства
для роста стека и области данных
Свободный
фрагмент
Рис. 4.5. Учет использования памяти: а — область памяти с пятью процессами и тремя
свободными фрагментами; б — соответствующая битовая карта; в — та же информация
в виде списка
становится
становится
становится
сможет работать на машине с объемом памяти 256 Мбайт, если тщательно про
думать, какие 256 Мбайт должны находиться в памяти в каждый момент време
ни. При этом по мере необходимости части программы, находящиеся на диске,
будут меняться местами с частями в памяти.
Виртуальная память вполне работоспособна и в многозадачной системе при
наличии множества одновременно загруженных в память программ. Когда про
грамма ждет перемещения в память очередной ее части, она находится в состоя
нии ожидания ввода-вывода и не работает, поэтому центральный процессор
может быть отдан другому процессу тем же самым способом, как в любой другой
многозадачной системе.
Очень простой пример того, как выполняется такого рода отображение, при
веден на рис. 4.8. Мы рассматриваем компьютер, который может формировать
16-разрядные адреса, от 0 до 64 К. Это — виртуальные адреса. У данного компью
тера есть только 32 Кбайт физической памяти, поэтому, хотя программы разме
ром 64 Кбайт писать можно, их нельзя целиком загрузить в память и запустить
на выполнение. Полная копия образа памяти программы размером до 64 Кбайт
должна присутствовать на диске, но в таком виде, чтобы ее можно было по мере
надобности переносить в память по частям.
Виртуальное
адресное пространство
Страничный блок
Рис. 4.8. Связь между виртуальными и физическими адресами,
поддерживаемая с помощью таблицы страниц
Физическай адрес
на выходе (24580)
Виртуальный адрес
на входе (8196)
Рис. 4.9. Внутренняя операция блока управления памятью в системе из 16 страниц по 4 Кбайт
Таблица страниц
второго уровня
б
Рис, 4,10, Структура двухуровневой таблицы страниц: а — 32-разрядный адрес с полями
двух таблиц страниц; б — таблицы страниц
Блокирование
кэша Изменение Присутствие/отсутствие
Обращение Защита
Рис- 4.11. Типичная запись таблицы страниц
Традиционная таблица
Хеш-таблица
216 -1
Индексирование Индексирование
по виртуальной по хеш-коду Виртуальная
странице виртуальной страницы страница
Страничный блок
Рис. 4.12. Сравнение обычной и инвертированной таблиц страниц
440 Глава 4. Управление памятью
что загруженной версией. Если оказывается, что страница (новая или обновляе
мая) не умещается в кэш, должно быть принято решение об удалении какой-ли
бо другой страницы из кэша. Методы выбора здесь аналогичны выбору страниц
в виртуальной памяти, за тем исключением, что веб-страницы никогда не моди
фицируются и не записываются обратно на веб-сервер. В системе с виртуальной
памятью страницы, находящиеся в основной памяти, могут быть «чистыми» или
«грязными».
Страница,
загруженная Последняя
первой загруженная
страница
А трактуется
как заново
загруженная
страница
б
Рис. 4.13. Иллюстрация работы алгоритма второго шанса: a — страницы, отсортированные
в порядке FIFO; б — список страниц, если ошибка отсутствия страницы произошла
в момент 20, а страница А имеет бит R, равный,0
после каждой команды. Кроме того, каждая запись в таблице страниц должна
иметь поле, достаточно большое для хранения значения счетчика. После каждого
обращения к памяти текущая величина счетчика С запоминается в записи табли
цы, соответствующей той странице, к которой произошла ссылка. А если возни
кает ошибка отсутствия страницы, операционная система проверяет все значе
ния счетчиков в таблице страниц и ищет наименьшее. Эта страница и является
дольше всего не использовавшейся.
Рассмотрим второй вариант аппаратной реализации алгоритма LRU. На машине
с п страничными блоками оборудование LRU может поддерживать матрицу разме
ром п х п бит, изначально равных нулю. Всякий раз при доступе к страничному
блоку k аппаратура сначала присваивает всем битам строки k единицу, а затем
приравнивает нулю все биты столбца k. В любой момент времени строка, двоич
ное значение которой наименьшее, является дольше всего не использовавшейся.
Работа этого алгоритма продемонстрирована на рис. 4.15, где рассматриваются
четыре страничных блока и следующий порядок обращения к страницам:
0123210323
После ссылки на страницу 0 мы получаем ситуацию, показанную на рис. 4.15, а;
после обращения к странице 1 — на рис. 4.15, б и т. д.
a бег д
0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 0 0 1 0 0
1 0 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0
1 0 0 1 0 0 0 1 0 0 0 0 1 1 0 1 1 1 0 0
1 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0
е ж з и к
Рис. 4.15. Алгоритм LRU с привлечением матрицы. Обращения к страницам происходят
в последовательности: 0, 1, 2, 3, 2, 1, 0, 3, 2, 3
интервале времени. Все, что мы можем сделать, — это выгрузить страницу 3, так
как к странице 5 также обращались двумя тактами раньше, а к странице 3 — нет.
а б в г д
Рис. 4.16. Алгоритм старения программно моделирует алгоритм LRU. Показаны шесть
страниц после пяти тактов часов
Рис. 4.17. Рабочий набор — это множество страниц, использованных при последних
к обращениях к памяти. Функция w(k, t) представляет собой размер рабочего
набора в момент времени t
4.6. Сегментация
Обсуждавшаяся до сих пор виртуальная память представляла собой одномерное
пространство, в котором виртуальные адреса идут один за другим от 0 до некото
рого максимума. Для решения многих задач наличие двух и более отдельных
виртуальных адресных пространств может быть лучше, чем одно. Например, по
мере трансляции формируются несколько структур данных компилятора.
1. Исходный текст, сохраненный для печати листинга (в пакетных системах).
2. Символьная таблица с именами и атрибутами переменных.
3. Таблица со всеми используемыми константами: целыми и с плавающей точкой.
4. Дерево грамматического разбора с результатами синтаксического анализа про
граммы.
5. Стек, используемый для процедурных вызовов внутри компилятора.
Во время компиляции каждая из первых четырех структур непрерывно растет.
Стек при компиляции непредсказуемо увеличивается или уменьшается. В одно
мерной памяти эти пять структур должны размещаться в смежных частях вирту
ального адресного пространства (рис. 4.20).
Рассмотрим, что происходит, если программа имеет исключительно большое
число переменных и обычное количество всего остального. Область адресного
пространства, предоставленная для символьной таблицы, может заполниться, но
в других структурах, скорее всего, останется пустым множество ячеек. Конечно,
4.6. Сегментация 457
} Свободно
Адресное пространство,
предоставленное Пространство, в данный
для дерева синтаксического • момент используемое
пространства деревом синтаксического
J анализа
При другом подходе можно поиграть в Робин Гуда, забирая пространство у струк
тур с излишками места и передавая их структурам с их недостатком. Такая пере
тасовка реализуема, но она аналогична управлению собственными оверлеями, что
в лучшем случае неудобно, а в худшем случае требует большого объема скучной
и непродуктивной работы.
На самом деле нужно найти подход, освобождающий программиста от управле
ния расширяющимися и сокращающимися структурами данных, так же как вир
туальная память избавляет программы от возни с оверлеями.
Простое и предельно обобщенное решение заключается в том, чтобы обеспечить
машину множеством полностью независимых адресных пространств, называемых
сегментами. Каждый сегмент содержит линейную последовательность адресов
от 0 до некоторого максимума. Длина каждого сегмента может быть любой от
нуля до разрешенного максимума. Различные сегменты могут быть различной
длины. Более того, длины сегментов могут меняться во время выполнения про
граммы. Длина сегмента стека может увеличиваться всякий раз, когда что-либо
помещается в стек, и уменьшаться при выборке данных из стека.
Поскольку каждый сегмент образует отдельное адресное пространство, разные
сегменты могут расти или сокращаться независимо друг от друга. Если стек,
находящийся в определенном сегменте, нуждается в увеличении адресного про
странства, он может получить его, потому что в его адресном пространстве нет
458 Глава 4. Управление памятью
больше ничего, что может стать препятствием для роста. Конечно, сегмент мо
жет заполниться, но сегменты обычно очень большие, поэтому такие инциденты
редки. Чтобы определить адрес в такой сегментированной, или двухмерной,
памяти, программа должна указать адрес, состоящий из двух частей: номера сег
мента и адреса внутри сегмента. Рисунок 4.21 иллюстрирует сегментированную
память, использующуюся для обсуждавшихся ранее структур данных компиля
тора. Здесь показаны 5 независимых сегментов.
Ж (3
Сегмент 4 Сегмент 4 . <зк>
(7 К) (7 К) Сегмент 5 Сегмент 5
(4 К) (4 К)
Сегмент 3
(8 К)
Сегмент 3 Сегмент 3 HillСегмент 5
(8 К) (8 К) Сегмент 6
(4 К)
(4 К)
Сегмент 6
Сегмент 2 Сегмент 2 Сегмент 2 Сегмент 2 (4 К)
(5 К) (5 К) (5 К) (5 К)
Сегмент 2
(5 К)
Сегмент 1 <зк>
(8 К) Сегмент 7 Сегмент 7 Сегмент 7 Сегмент 7
(5 К) (5 К) (5 К) (5 К)
Сегмент О Сегмент О Сегмент 0 Сегмент 0 Сегмент 0
(4 К) (4 К) (4 К) (4 К) (4 К)
a б в г д
Рис. 4.22. Решение проблемы фрагментации: a-r— развитие внешней фрагментации;
д — устранение фрагментации путем уплотнения
Биты 1 2
Индекс
Уровень
О = GDT/1 = LDT
привилегированности (0-3)
Рис. 4.23. Селектор в системе Pentium
462 Глава 4. Управление памятью
База 24-31 (3 D
°1 Предел
16-19
Р DPL S Тип База 16-23
Ч------------------------------------------------ 32 бита-------------------------------------------------►
Относительная
адресация
Рис. 4.24. Дескриптор программного сегмента в системе Pentium. Сегменты данных
немного отличаются
Если режим замещения страниц отключен (за счет бита в глобальном управляю
щем регистре), линейный адрес интерпретируется как физический адрес и посы
лается в память для чтения или записи. Таким образом, при отключении режима
замещения страниц мы получаем чистую схему сегментации с базовым адресом
каждого сегмента, выдаваемым его дескриптором. Сегментам разрешено пере
крываться случайным образом, возможно потому, что реализация контроля за
тем, чтобы они не пересекались, была бы слишком трудоемкой и заняла бы слиш
ком много времени.
В то же время в режиме замещения страниц линейный адрес интерпретируется
как виртуальный адрес и отображается на физический адрес с помощью таблицы
страниц практически так же, как в наших предыдущих примерах. Единственная
серьезная трудность заключается в том, что при 32-разрядном виртуальном адресе
и странице размером 4 Кбайт сегмент может содержать 1 млн страниц, поэтому
имеет место двухуровневое отображение с целью уменьшения размера таблицы
страниц для маленьких сегментов.
У каждой работающей программы есть страничный каталог, состоящий из 32-раз-
рядных записей в количестве 1024. Он расположен по адресу, хранящемуся в гло
бальном регистре. Каждая запись в каталоге ссылается на таблицу страниц, так
же содержащую 1024 записей (тоже 32-разрядных). Записи в таблицах страниц,
в свою очередь, указывают на страничные блоки. Эта организация продемонст
рирована на рис. 4.26.
464 Глава 4. Управление памятью
Линейный адрес
Биты 10 10 12
б
Рис. 4.26. Отображение линейного адреса на физический
рис. 4.26, после чего TLB обновляется. Система обладает хорошей производи
тельностью до тех пор, пока обращения к отсутствующим в TLB страницам про
исходят относительно редко.
Если немного поразмыслить над механизмом замещения страниц, можно прий
ти к выводу о том, что смысла в использовании нулевого значения поля базы
нет. Единственное, для чего нужно это поле, — обеспечить смещение, позволяющее
использовать записи, находящиеся в середине, а не в начале страничного катало
га. Фактически база требуется лишь для поддержки «чистой» (без замещения
страниц) сегментации, а также для совместимости с процессором 286, в котором
режим замещения страниц всегда отключен.
Также следует отметить, что даже если приложение не требует сегментации, а до
вольствуется единым разбитым на страницы 32-разрядным адресным простран
ством, эта модель все равно работает. Все сегментные регистры могут быть на
строены тем же самым селектором, в дескрипторе которого поле базы равно О,
а поле лимита установлено на максимум. Тогда при использовании единого ад
ресного пространства смещение команды будет линейным адресом — в сущно
сти, это обычное замещение страниц. Фактически все современные операцион
ные системы для компьютера Pentium работают таким образом. Система OS/2
была единственной, в которой были задействованы все возможности архитекту
ры блока управления памятью (MMU) компании Intel.
В конце концов, кто-то должен похвалить разработчиков Pentium. При постав
ленных перед ними противоречивых задачах — реализовать «чистое» замещение
страниц памяти, «чистую» сегментацию и страничные сегменты и в то же время
обеспечить совместимость с процессором 286, а кроме того, сделать все это эф
фективно — то, что у них получилось, на удивление просто и понятно.
Мы, хотя и кратко, но целиком описали архитектуру виртуальной памяти про
цессоров Pentium и теперь следует сказать несколько слов о защите, так как эта
тема тесно связана с виртуальной памятью. Pentium поддерживает четыре уров
ня защиты, где уровень 0 является наиболее привилегированным, а уровень 3 —
наименее привилегированным (рис. 4.27). В каждый момент времени работаю
щая программа находится на определенном уровне, что отмечается 2-разрядным
полем в его слове состояния программы (PSW). Каждый сегмент в системе так
же имеет свой уровень.
До тех пор пока программа сама ограничивает использование сегментов на своем
собственном уровне, система прекрасно работает. Разрешаются попытки получе
ния доступа к данным более высокого уровня. Попытки доступа к данным более
низкого уровня запрещены и вызывают прерывания. Попытки вызвать процеду
ры различного уровня (более высокого или низкого) позволяются, но тщательно
контролируются. Чтобы сделать межуровневый вызов, инструкция CALL должна
содержать селектор вместо адреса. Этот селектор определяет дескриптор, назы
ваемый шлюзом вызова (call gate) и передающий адрес вызываемой процедуры.
Таким образом, попасть в середину произвольного сегмента кода другого уровня
невозможно, открыты лишь официальные точки входа.
466 Глава 4. Управление памятью
Типичное
собственные нужды. Например, драйвер памяти может запросит память под вир
туальный диск. Это возможно лишь на этапе инициализации системы.
На рис. 4.28 показаны оба варианта выделения памяти. На рис. 4.28, а мы ви
дим в памяти два процесса, Л и В. Если процесс А разветвляется, мы попадаем
в ситуацию, представленную на рис. 4.28, б. Дочерний процесс является точной
копией процесса А. Если теперь дочерний процесс выполнит файл С, память
Стек
Сегмент стека
растет вниз
Всего
занято
памяти Сегмент данных растет
Данные + bbs вверх (или вниз), когда
делается системный
Код вызов BRK
б
Рис. 4.29. Распределение памяти для программы: а — программа хранится на диске в виде
файла; б — внутреннее распределение памяти для одного процесса. На обеих частях
рисунка в нижней части расположены нижние адреса
4.7. Знакомство с менеджером процессов в MINIX 3 471
Если программист знает, что общий объем памяти, необходимый для стека и дан
ных программы в файле а. out, не превышает 10 Кбайт, то при помощи следую
щей команды он может изменить поле заголовка исполняемого файла:
chmem = 10240 a.out
После этого вызов ехес будет выделять только 10 240 байт дополнительной па
мяти. Так, для описанного примера будет выделено 16 Кбайт памяти. Из этой
памяти верхний килобайт отводится под стек, а 9 Кбайт образуют свободную об
ласть для будущего роста стека и (или) области данных.
Для программ с раздельными адресными пространствами кода и данных (у кото
рых компоновщик устанавливает специальный бит в заголовке программы) по
ле общего размера в заголовке относится только к стеку и данным. Так, при за
грузке программы с 4 Кбайт кода, 2 Кбайт данных, 1 Кбайт стека и суммарным
размером 64 Кбайт будет выделено 68 Кбайт памяти (4 Кбайт на код, 64 Кбайт
образуют адресное пространство данных, а 61 Кбайт останется для роста стека
и данных). Граница сегмента данных может быть изменена только при помощи
системного вызова brk. При выполнении этого вызова проверяется, не упирает
ся ли новая граница сегмента данных в нижнюю границу стека, и соответствую
щие изменения вносятся во внутренние таблицы. Это делается исключительно
внутри памяти, выделенной процессу, поскольку у операционной системы не за
прашивается никаких новых блоков. Если же после изменения границы сегмент
данных будет пересекаться со стеком, вызов завершается ошибкой.
Тут нужно упомянуть небольшую семантическую сложность. Когда мы говорим
«сегмент», мы имеем в виду область памяти, определяемую операционной систе
мой. Но у процессоров Intel 80x86 есть специальные внутренние сегментные ре
гистры и (у более новых из них) таблицы дескрипторов сегментов, обеспечи
вающие аппаратную поддержку «сегментов». Концепция сегмента в архитектуре
Intel сходна с тем, как сегменты определяются в MINIX 3, но это — не одно и то
же. Все ссылки на сегменты в этом тексте следует рассматривать в контексте их
определения в MINIX 3. Когда мы будем говорить об аппаратных сегментах, мы
явно укажем сегментные регистры и дескрипторы сегментов.
Это предупреждение может быть обобщено. Разработчики аппаратного обеспече
ния часто целенаправленно стараются реализовать поддержку на своем оборудова
нии конкретных операционных систем. Поэтому терминология описания регистров
и других аспектов архитектуры процессора обычно отражает то, как все это будет
использоваться. Подобные возможности часто полезны для разработчиков опера
ционной системы, но практика не обязательно соответствует идеям производителя
оборудования. В результате термины, звучащие одинаково, но имеющие разное зна
чение для операционной системы и оборудования, могут привести к непониманию.
Таблица 4.3. Типы и параметры сообщений для менеджера памяти и ответные значения
Тип сообщения Параметры________________________________ Ответное значение
fork Нет PID потомка
exit Код возврата При успехе без ответа
wait Нет Состояние
waitpid Нет Состояние
brk Новый размер Новый размер
exec Указатель на исходный стек При успехе без ответа
kill Идентификатор процесса и сигнал Состояние
alarm Время ожидания в секундах Оставшееся время
pause Нет При успехе без ответа
sigaction Номер сигнала, код операции, старый код Состояние
операции
sigsuspend Маска сигналов При успехе без ответа
sigpending Нет Состояние
sigprocmask Способ изменения, новый набор, старый набор Состояние
sigreturn Контекст Состояние
getuid Нет Идентификатор UID и его
действующее значение
getgid Нет Идентификатор GID и его
действующее значение
getpid Нет Идентификатор PID и его
действующее значение
setuid Новый идентификатор UID Состояние
setgid Новый идентификатор GID Состояние
setsid Новый идентификатор SID Группа процессов
getpgrp Новый идентификатор GID Группа процессов
ptrace Запрос, PID, адрес, данные Состояние
reboot Действие (останов, перезагрузка, сбой) При успехе без ответа
svrctl Запрос, данные (в зависимости от функции) Состояние
getsysinfo Запрос, данные (в зависимости от функции) Состояние
getprocnr Нет Номер процедуры
memalloc Размер, указатель на адрес Состояние
memfree Размер, адрес Состояние
getpriority PID, тип, значение Приоритет
setpriority PID, тип, значение Приоритет
gettimeofday Нет Время, время работы
4.7. Знакомство с менеджером процессов в MINIX 3 473
Вызовы fork, exit, wait, waitpid, brk и exec тесно связаны с выделением
и освобождением памяти. Вызовы kill, alarm и pause связаны с сигналами,
такими как sigaction, sigsuspend, sigpending, sigmask и sigreturn.
Они оказывают влияние и на память, так как при завершении процесса по сигна
лу занимаемая этим процессом память высвобождается. Семь вызовов get/set
не имеют никакого отношения к управлению памятью, но, очевидно, связаны
с управлением процессами. Остальные вызовы с одинаковым успехом могли бы
обрабатываться и менеджером процессов, и файловой системой, поскольку дру
гих компонентов, которые могли бы их обслужить, нет. Они были помещены в код
менеджера процессов просто потому, что код файловой системы и без того доста
точно объемен. Вызов ptrace, применяемый при отладке, а также вызовы time,
stime и times попали сюда по той же причине.
Вызов reboot действует в масштабе всей системы, однако его главным назначени
ем является передача сигналов для контролируемого завершения всех процессов,
поэтому было логично поместить его в менеджер процессов. То же касается и вызо
ва svrctl, основное применение которого — включение и отключение подкачки.
Возможно, вы заметили, что последние два упомянутых вызова, reboot и svrctl,
не приведены в табл. 1.1. То же касается и других системных вызовов, представ
ленных в табл. 4.3: getsysinfo, getprocnr, memalloc, memfree и getset-
priority. Все они не предназначены для обычных пользовательских процес
сов и не являются частью стандарта POSIX. Они нужны лишь операционным
системам, подобным MINIX 3. В системах с монолитным ядром действия, вы
полняемые этими вызовами, могли бы осуществляться функциями, встроенны
ми в ядро. Однако в MINIX 3 традиционные компоненты операционной системы
выполняются в пользовательском пространстве, что и требует дополнительных
системных вызовов. Некоторые системные вызовы несут чуть больше функций,
чем обеспечение интерфейсов с вызовами ядра (имеются в виду вызовы, запра
шивающие различные службы ядра у системного задания).
Как отмечалось в главе 1, несмотря на наличие библиотечной подпрограммы
sbrk, одноименного системного вызова не существует. Подпрограмма вычисля
ет объем необходимой памяти, инкрементируя или декрементируя текущий раз
мер согласно передаваемому параметру, и выполняет вызов brk для установки
нового размера. Аналогично, не существует отдельных системных вызовов для
функций geteuid и getegid. Вызовы getuid и getpid возвращают соответ
ственно эффективный и реальный идентификаторы. Аналогично, getpid воз
вращает идентификаторы вызывающего процесса и его родителя.
Ключевой структурой данных для обработки сообщений является call_vec,
определяемая в файле table. с. Она содержит указатели на подпрограммы, об
рабатывающие все типы сообщений. Когда сообщение попадает в менеджер про
цессов, команды в главном цикле извлекают из него тип сообщения и записыва
ют его в глобальную переменную call_nr. Позже это значение используется
как индекс в таблице call_vec, и по нему находится адрес подпрограммы для
обработки прибывшего сообщения. Затем эта подпрограмма отрабатывает, выпол
няя системный вызов. Значение, которое возвращает обработчик, отправляется
474 Глава 4. Управление памятью
обратно, чтобы сообщить о выполнении или ошибке. Этот механизм подобен ме
ханизму обработки системных вызовов (шаг 7 на рис. 1.12), но только в адрес
ном пользовательском пространстве, а не в пространстве ядра.
Процессы в памяти
Таблица процессов менеджера процессов называется mproc, она определена
в файле src/servers/pm/mproc .h. В этой таблице содержатся поля, связанные
с выделением памяти, а также некоторые дополнительные сведения. Самым важ
ным полем является массив mp_seg, у которого есть три записи, для сегмента
текста (кода), данных и стека. Все его элементы представляют собой структуры,
содержащие виртуальный и физический адреса, а также длину сегмента, причем
эти величины измеряются не в байтах, а в кликах. Размер клика (минимального
блока памяти) зависит от реализации. В ранних версиях MINIX используется
значение 256 байт, а в MINIX 3 — 1024 байта. Все сегменты начинаются на гра
нице клика и содержат целое число кликов.
Способ записи информации о выделении памяти иллюстрирует рис. 4.30. На ри
сунке показан процесс с такой структурой: 3 Кбайт кода, 4 Кбайт данных, зазор
имеет величину 1 Кбайт, а после него следует стек объемом 2 Кбайт. Всего вы
делено 10 Кбайт памяти. На рис. 4.30, б показано, что хранится в полях длины
и виртуального и физического адресов для этих сегментов при условии, что
процесс не разделяет адресные пространства кода и данных. В этой модели
размер сегмента кода всегда считается равным нулю, а сегмент данных содержит
и данные, и код. Когда показанный на рисунке процесс обращается к виртуаль
4.7. Знакомство с менеджером процессов в MINIX 3 475
Адрес (Ьм^миРПк
R и пт\/я
LJrl|J к н kJ й
1 yOUпIDHDIrl М* Ы гм1
ИЮ И1 *10 V |\’И| Л пмия
fjjj 1И1ПС1
(шестнадцатеричный)
Стек 0x8 OxdO 0x2
210 К (0x34800)
Стек Данные 0 0хс8 0x7
208 К (0x34000)
Код 0 0хс8 0
'///% ////>/
207 К(ОхЗЗсОО)
б
Данные
— 203 К(0х32с00) Виртуальный Физический Длина
Код Стек 0x5 OxdO 0x2
— 200 К (0x32000) Данные 0 Oxcb 0x4
ip У Код 0 0хс8 0x3
а в
Рис. 4.30. Способ записи информации о выделении памяти: а — процесс в памяти;
б — представление процесса с единым адресным пространством данных и кода;
в — представление процесса с раздельными пространствами данных и кода
Общий код
При выполнении процесса содержимое его областей данных и стека может ме
няться, но код не меняется никогда. Часто случается, что несколько процессов
выполняют одну и ту же программу, например, несколько пользователей могут
работать с одной оболочкой. Поэтому общий код повышает эффективность памя
ти. Когда системный вызов ехес собирается загрузить процесс, он открывает
файл с образом этого процесса и считывает заголовок. Если у процесса раздель
ные адресные пространства кода и данных, среди всех ячеек таблицы mproc осу
ществляется поиск по полям mp_dev, mp_ino, mp_ctime. Эти поля содержат
информацию о номере индексного узла и времени модификации образов, испол
няемых другими процессами. Если обнаруживается процесс, который уже вы
полняет нужную программу, то выделять память под еще одну копию кода не
нужно. Вместо этого в карту памяти нового процесса в поле mp_seg [Т] записы
вается указатель на ту область памяти, где уже хранится код, а память выделяет
ся только под данные и стек (рис. 4.31). Если загруженного образа найдено не
было или адресные пространства кода и данных объединены, память выделяется
согласно рис. 4.30 и заполняется данными с диска.
4.7. Знакомство с менеджером процессов в MINIX 3 477
Стек
Данные
Код
Стек
Данные
Код
б
Рис- 4-31- Использование общего кода: а — карта памяти для раздельных адресных
пространств кода и данных, как на предыдущем рисунке; б — распределение памяти после
запуска второго процесса, выполняющего тот же код; в — карта памяти второго процесса
Листинг 4-1 ■ Список свободных участков представляет собой массив структур hole
PRIVATE struct hole {
struct hole *h__next; /* Указатель на следующий элемент списка дыр */
phys_clicks h_base; /* Начало списка */
phys_clicks h_len; /* Размер списка*/
} hole[NR_HOLES] ;
\0 t S а 8188 \0 t s a 8188
/ г S и 8184 / г s u 8184
/ = Е М 8180 / = E M 8180
О Н \0 с 8176 О Н \0 c 8176
g \0 с 8172 g \0 c 8172
f \0 I 8168 t \0 I 8168
- \о S I 8164 - \о s I 8164
0 8160 0 8160
8178 8156 8178 8156
0 8152 0 8152
8174 8148 8174 8148
8170 8144 8170 8144
8167 8140 8167 8140
8164 8136 8164 8136
8156 8132
envp 8136 8128
argv 4 8124
argc return 8120
а б в г
Рис. 4.32. Создание стека в пользовательском пространстве: а — массивы, передаваемые
подпрограмме execve; б — созданный стек; в — стек после перемещения менеджером
памяти; г — стек, который процедура видит в начале выполнения
Если программист в конце кода функции main не сделал вызов exit, то после
ее завершения управление передается обратно в подпрограмму runtime. Опять
же, с точки зрения компилятора, main — обычная функция, и для возврата из
нее компилятор генерирует стандартный код. Большая часть кода 32-разрядной
версии crtso приведена в листинге 4.2. Комментарии поясняют выполняемые
действия. Не вошли в этот листинг лишь фрагменты кода, инициализирующие
окружение (на случай, если это не сделает программист), загружающие из стека
помещенные туда регистры, а также несколько строк, устанавливающие флаг на-
личия/отсутствия математического сопроцессора. Весь код вы можете просмот
реть, открыв файл src/lib/i386/rts/crtso. s.
Листинг 4.2. Ключевая часть стартовой подпрограммы runtime
push ecx ; push environ
push edx ; push argv
push eax ; push argc
call _main ; main(argc, argv, envp)
push eax ; push exit_status
call _exit
hit ; если вызов exit не уда вызывается прерывание
484 Глава 4. Управление памятью
Таблица 4.4. Сигналы MINIX 3, регламентированные POSIX. Знак (*) означает, что сигнал
зависит от аппаратной поддержки, сигналы с пометкой (М) не описаны в POSIX и введены
в MINIX 3 для поддержания совместимости с устаревшим кодом. Сигналы ядра являются
специфичными для MINIX 3 и предназначены для информирования системных процессов
о событиях системы. В таблицу не попали несколько устаревших имен и синонимов
Сигнал Описание Источник
SIGHUP Отбой Системный вызов kill
SIGINT Прерывание Ядро
SIGQUIT Завершение Ядро
SIGILL Недопустимая инструкция Ядро (*)
4.7. Знакомство с менеджером процессов в MINIX 3 487
на каждом этапе выполнения стек выглядит как обычный стек процесса с собст
венными локальными переменными после адреса возврата.
Главное, что должен сделать вызов sigreturn, — это вернуть все в исходное со
стояние, то есть к моменту получения сигнала. Важнее всего восстановить кадр
стека процесса, для чего и используется его копия, сохраненная в собственном
стеке процесса. В результате после завершения sigreturn процесс возвращает
ся в исходное состояние (рис. 4.33, г).
По умолчанию большинство сигналов приводят к завершению процесса. Забо
тится об этом менеджер процессов, завершающий процесс, когда сигнал не игно
рируется по умолчанию или процесс-приемник не требует его блокировать, об
рабатывать или игнорировать. Если завершения процесса ожидает его родитель,
процесс завершается, и соответствующая информация удаляется из таблицы
процессов. Если родитель пренебрегает своим долгом, процесс превращается в зом
би. Кроме того, для некоторых типов сигналов (например, sigquit) менеджер
процессов записывает в текущий каталог дамп памяти процесса.
Легко может случиться так, что получивший сигнал процесс находится в состоя
нии блокировки, скажем, читает с терминала при помощи вызова read, когда на
терминал не поступает данных. Если процесс не указал, что сигнал должен обра
батываться, он просто завершается обычным образом. Но если сигнал обрабаты
вается, закономерен вопрос: что должен делать процесс после того, как обработано
прерывание сигнала. Должен ли он вернуться в состояние ожидания или про
должить работу со следующей инструкции?
В MINIX 3 делается следующее: системный вызов завершается с кодом возвра
та EINTR, поэтому процесс может понять, что он был прерван сигналом. Узнать
о том, что процесс заблокирован на системном вызове, не так просто. Менеджеру
процессов придется спрашивать об этом файловую систему.
Такое поведение предполагается стандартом POSIX, который позволяет вызову
read возвращать байты, считанные до прихода сигнала, хотя и не жестко пред
писано. Кроме того, возврат с кодом EINTR позволяет легко реализовать таймер
и обработать сигнал sigalarm, например, чтобы завершить операцию login и по
весить трубку модема, если пользователь некоторое время (тайм-аут) не отвечает.
Поле mp_f lags, как показано в конце файла, необходимо для хранения разно?
образных битовых сочетаний. В нем хранится беззнаковое целое, 16-разрядное
для самых старых процессоров и 32-разрядное для процессоров 386 и выше.
Последнее поле таблицы процессов называется mp_procargs. Когда запускается
новый процесс, строится стек, подобный показанному на рис. 4.32, и указатель
на массив argv нового процесса сохраняется в этой переменной. Например, на
рис. 4.32 в поле будет сохранено значение 8164, благодаря чему, цока выполняется
команда 1s, команда ps может вывести содержимое командной строки:
Is -1 f.c g. с
Благодаря данному массиву обеспечивается значительная гибкость при обработ
ке сигналов.
Поле mp_swapq не используется в рассматриваемой версии MINIX. Оно приме
няется при включенной подкачке и указывает на очередь ожидающих подкачки
процессов. Поле mp_reply предназначено для ответного сообщения. В более
ранних версиях MINIX такое поле было всего одно, определялось оно в файле
glo.h и компилировалось вместе с table, с. В MINIX 3 память под ответные
сообщения предоставляется каждому процессу. Это позволяет менеджеру про
цессов приступать к обработке следующего входящего сообщения, если ответ на
предыдущее сообщение не может быть отправлен сразу по окончании его генера
ции. Менеджер процессов не способен обрабатывать два запроса одновременно,
но при необходимости может откладывать отправку ответов, пытаясь послать все
активные ответы каждый раз по завершении обработки запроса.
Последние два элемента таблицы процессов можно считать украшательствами.
Поле mp_nice содержит значение, позволяющее пользователям снизить приори
тет процесса, например, для того, чтобы другой, более важный, процесс мог вы
теснить его. Тем не менее MINIX 3 использует это поле для собственных нужд,
назначая различные приоритеты различным системным процессам (драйверам
и серверам) в зависимости от их потребностей. Поле mp_name удобно для отлад
ки, помогая программисту найти запись таблицы процессов в дампе памяти.
Имеется системный вызов, выполняющий поиск в таблице процессов по задан
ному имени и возвращающий соответствующий идентификатор.
Обратите внимание на то, что часть таблицы процессов, принадлежащая ме
неджеру процессов, объявлена как массив размером NR_PROCS (строка 17655).
Вспомните, что часть, принадлежащая ядру, имеет размер NR_TASKS + NR_
PROCS и объявлена в файле kernel/proc .h (строка 5593). Как было отмечено
ранее, процессы, являющиеся частью ядра, невидимы для компонентов операци
онной системы, находящихся в пользовательском пространстве, например, менед
жеру процессов. На самом деле, эти процессы не играют ключевой роли.
Следующий файл, param.h (строка 17700), содержит макросы, помогающие
формировать сообщения для многих системных вызовов. Кроме того, здесь же
описаны четыре макроса для формирования ответных сообщений. В любом фай
ле, куда включен файл param. h, может быть указано такое выражение:
k = m_in.pid;
4.8. Управление памятью в MINIX 497
Перед тем как продолжить, давайте обратимся к файлу table. с (строка 17800).
При его компиляции получается объектный файл, в котором выделяется место
для хранения глобальных переменных и структур, объявленных с директивой
EXTERN. Такие конструкции мы видели в файлах glo.h и mproc .h. Благодаря
тому, что в этом файле присутствует следующее выражение, макрос EXTERN раз
ворачивается в пустую строку:
#define _TABLE
Это — тот же самый механизм, который мы могли наблюдать в коде ядра. Как
уже было отмечено, переменная core_name объявлена в файле glo.h с ключе
вым словом extern, а не EXTERN. Теперь становится ясно, почему: core_name
объявляется со строкой инициализации, а инициализация в определении extern
невозможна.
Еще один важный элемент файла table. с — массив call_vec (строка 17815).
Он также инициализирован, а потому в glo.h его нельзя было бы объявлять
с ключевым словом EXTERN. С его помощью номер системного вызова преобра
зуется в адрес выполняющей его функции, номер вызова играет роль индекса
в массиве. Если в сообщении указан несуществующий номер системного вызова,
управление передается функции no_sys, которая просто возвращает код ошиб
ки. Несмотря на то что при объявлении массива call_vec применяется макрос
„PROTOTYPE, в действительности это — описание не прототипа, а инициализи
рованного массива. Но так как он же массив указателей на функции, проще всего
получить код, компилируемый как классическим (Керниган и Ричи), так и стан
дартным компилятором С при помощи макроса „PROTOTYPE.
И наконец, последнее замечание о заголовочных файлах. Поскольку MINIX 3 до
сих пор находится в стадии активной разработки, некоторые «острые углы» до
сих пор не «сглажены». Один из них состоит в том, что некоторые исходные файлы
в каталоге рш/ включают заголовочные файлы из каталога ядра. Если не знать
об этом, можно столкнуться с проблемами при поиске некоторых важных опреде
лений. Вероятно, определения, используемые более чем одним из основных компо
нентов MINIX 3, следует разместить в заголовочных файлах каталога include/.
Строго говоря, sys_get image является не вызовом ядра, а одним из более 10 мак
росов, определенных в файле include/minix/syslib.h и обеспечивающих
простой интерфейс к вызову ядра sys_getinfo. Процессы ядра невидимы из
пользовательского пространства, и части таблицы процессов, принадлежащие
менеджеру процессов и файловой системе, не нуждаются в инициализации со
стороны компонентов ядра. Фактически место под записи процессов ядра не ре
зервируется. Каждый из них имеет отрицательный номер (индекс в таблице про
цессов) и игнорируется при проверке в строке 18202. Кроме того, вызывать
функцию patch_mem_chunks для процессов ядра не обязательно; при выделе
нии памяти под ядро потребности его процессов учитываются.
Как системные, так и пользовательские процессы необходимо добавить в табли
цу процессов, хотя работа с ними ведется по-разному (строки 18210-18219).
Единственным пользовательским процессом, включенным в состав загрузочного
образа, является init, поэтому в строке 18210 выполняется проверка для INIT_
PROC_NR. Все остальные процессы, входящие в загрузочный образ, — системные.
Системные процессы являются особыми: они не могут быть вытеснены, каждый
из них обладает собственной записью в таблице priv ядра и набором привиле
гий, задаваемым флагами. Для каждого процесса определены надлежащие пара
метры обработки сигналов по умолчанию, при этом между системными про
цессами и init имеются определенные различия. Далее карта памяти каждого
процесса извлекается из ядра при помощи функции get_mem_map, совершаю
щей вызов ядра sys_getinfo, и patch_mem_map, соответствующим образом
изменяющей массив mem_chunks (строки 18225-18230).
Наконец, файловой системе передается сообщение, с помощью которого каждый
процесс инициализируется в принадлежащей ей части таблицы процессов (стро
ки 18233-18236). Сообщение содержит только номер и идентификатор процесса;
этого достаточно, чтобы инициализировать запись в таблице файловой системе,
поскольку все процессы загрузочного образа принадлежат суперпользователю
и могут получить одинаковые значения по умолчанию. Каждое сообщение от
правляется при помощи операции send, поэтому ответа на него не ожидается.
После передачи сообщения имя процесса отображается на консоли (строка 18237):
Building process table: pm fs rs tty memory log driver init
NONE в качестве номера процесса указывает файловой системе на то, что все сис
темные процессы инициализированы, можно выйти из цикла и послать синхрони
зирующее сообщение для снятия блокирования с менеджера процессов.
После этого файловая система может продолжить собственную инициализацию,
а инициализация менеджера процессов почти завершена. В строке 18253 вызы
вается функция mem_init. Она инициализирует связанный список свободных
областей памяти и нужные переменные, которые будут использованы для управ
ления памятью в процессе работы системы, на основе данных массива mem_
chunks. Управление памятью в обычном режиме начинается после того, как на
консоль выводится сообщение, содержащее общий объем памяти, память, заня
тую MINIX 3, и объем свободной памяти:
Physical memory: total 63996 КВ, system 12834 КВ, free 51162 КВ
а б
Рис. 4.35. Иллюстрация механизма завершения процесса: а — процесс 12 собирается
завершиться; б — ситуация после его завершения
Когда родитель делает вызов wait или waitpid, управление передается сле
дующей функции, do_waitpid (строка 18598). Параметры этих двух систем
ных вызовов различны, ожидаемые действия также различны, но благодаря
специальной настройке значений внутренних переменных (строки 18613-18615),
функция do_waitpid может выполнять оба вызова. После того как присвоены
4.8. Управление памятью в MINIX 505
При этом «волшебная» строка сценария указывает, что его следует интерпрети
ровать с помощью оболочки Bourne:
#! /bin/sh
\0 t 52
Массив
S а / г 48
переменных
S и / = 44
Е М О Н 40
\0 1 f \0 36
h S S 32
НОМЕ = /usr/ast
\0 h S / 28
п i b / 24
0 20
40 16
Действующий
0 12
массив
аргументов 37 8
О 32 4
>f1 24 О
-> s.sh
■> /bin/sh
а
Рис. 4.36. Обработка сценария: а — массивы, переданные вызову execve, и стек, созданный
при исполнении сценария; б — вид массивов и стека после обработки функцией patch_stack.
Имя сценария передается программе, интерпретирующей сценарий
Оповещения и таймеры
Системный вызов alarm выполняется функцией do_alarm (строка 19769). Она
вызывает следующую функцию, set_alarm, указав нулевое время оповещения.
Код set_alarm вынесен в отдельную функцию потому, что он также использу
ется для отключения таймера, когда процесс завершается, а таймер еще работает.
Функция sig_alarm работает с таймерами, поддерживаемыми внутри менед
жера процессов. Сначала она определяет, установлен ли таймер от имени за
прашивающего процесса, и, если да, истек ли он. Такие проверки позволяют сис
темному вызову вернуть оставшееся время предыдущего оповещения в секундах
или нуль, если таймер не установлен. Комментарий описывает некоторые про
блемы, связанные с большими тайм-аутами. В строке 19818 весьма неприятный
на вид код преобразует время в тики, умножая аргумент, переданный при вызове
(время в секундах), на константу HZ — число тактов таймера в секунду. Для того
чтобы привести результат к требуемому типу clock_t, нужно трижды преобра
зовать его тип. В следующей строке тип clock_t переменной ticks преобразу
ется обратно в unsigned long. Затем результат сравнивается с преобразова
нием исходного значения аргумента в unsigned long. Если обнаруживается
несовпадение, то запрошенное время вызывает выход за пределы значений одно
го из используемых типов данных, и вместо него подставляется значение, указы
вающее бесконечное время. Наконец, вызывается одна из функций pm_set_
timer или pm_cancel_timer, соответственно добавляющая и удаляющая тай
мер из очереди менеджера процессов. Ключевым аргументом последнего вызова
является cause_sigalarm — сторожевая функция, которая должна быть вы
полнена по истечении таймера.
Взаимодействие с таймерами, поддерживаемое в пространстве ядра, скрыто в вы
зовах подпрограмм pm_XXX_timer. Любой запрос оповещения, в конечном сче
те, вызывающий его генерацию, обычно приводит к запросу установки таймера
в пространстве ядра. Единственным исключением является ситуация, в которой
несколько запросов оповещения возникают одновременно. Однако процессы
могут отменять оповещения или завершать работу до истечения установленных
ими таймеров. Вызов ядра, устанавливающий таймер в пространстве ядра, дол
жен выполняться лишь при изменении таймера, находящегося во главе очереди
таймеров менеджера процессов.
Когда таймер, стоящий в очереди в пространстве ядра и установленный от имени
менеджера процессов, истекает, системное задание информирует об этом менедже
ра процессов, посылая ему уведомление, а тот обнаруживает его в своем главном
514 Глава 4. Управление памятью
Рис. 4.37. Сообщения при работе таймера. Самые главные из них: 1 — пользователь
делает вызов alarm; 3 — менеджер процессов обращается к системному заданию,
чтобы установить таймер; 6 — таймер информирует менеджер процессов
об истечении таймера; 7 — менеджер процессов запрашивает сигнал
для пользователя; 9 — обработчик завершается вызовом sigreturn
Этот вызов означает, что указанный сигнал должен быть отправлен всем процессам
в текущей группе (то есть всем процессам, запущенным с того же терминала).
Сигналы, исходящие от ядра и системного вызова reboot, также могут затраги
вать несколько процессов. По этой причине check_sig в цикле перебирает все
процессы из таблицы процессов, выбирая из них потенциальных получателей
(строки 19996-20026). В цикле содержится много тестов, и только если все они
пройдены, при помощи sig_proc отправляется сигнал (строка 20023).
Еще одна функция, которая несколько раз вызывается в рассмотренном нами ко
де, называется check_pending (строка 20036). Она перебирает все биты в би
товой карте mp_sigpending процесса, сверяясь с битовыми картами с помо
щью функций do_sigmask, do_sigretrun и do_sigsuspend, и смотрит, не
была ли снята блокировка с одного из задержанных сигналов. Обнаружив пер
вый такой сигнал, она отправляет его. Так как все обработчики событий со вре
менем вызывают функцию do_sigreturn, все разблокированные активные сиг
налы в конце концов доставляются.
Процедура unpause (строка 20065) необходима для отправки сигналов про
цессам, приостановленным на одном из вызовов pause, wait, read, write или
sigsuspend. Выяснить, обусловлена ли остановка вызовом pause, wait или
sigsuspend, можно, сверившись с таблицей процессов менеджера процессов.
Если выясняется, что ни один из этих вызовов не является причиной остановки,
запрос передается файловой системе, которая при помощи собственной функции
do_ unpause проверяет, был ли процесс приостановлен для чтения или записи.
В любом случае результат одинаков: незаконченный вызов завершается с ошиб
кой, а процесс вновь запускается и может обработать сигнал.
Последняя процедура в этом файле называется dump_core (строка 20093). Она
записывает на диск дампы памяти. Дамп состоит из заголовка, содержащего ин
формацию о размере занимаемых процессом сегментов, всей информации состоя
ния процесса (это копия записи процесса из таблицы процессов ядра) и образов
всех сегментов. Отладчик может прочитать эту информацию, чтобы помочь про
граммисту определить, что пошло не так в работе программы.
Код записи в файл прост. Здесь снова возможна проблема, упомянутая в предыду
щем разделе, но теперь в несколько иной форме. Чтобы гарантировать, что записы
ваемый в дамп сегмент стека содержит самую последнюю информацию, вызывает
ся функция adjust (строка 20010). Из-за проверки границы безопасности этот
вызов может «провалиться». Правда, дамп записывается в любом случае, незави
симо от успеха вызова, но информация о стеке может оказаться неправильной.
Установить GID или UID несколько сложнее, чем просто прочитать. Сначала
нужно проверить, уполномочен ли процесс изменять эти значения. Если проверка
пройдена, об изменении GID или UID необходимо информировать файловую сис
тему, так как эти значения влияют на защиту файлов. Вызов sets id создает новый
сеанс; этого нельзя делать процессу, который уже является лидером группы, и со
ответствующая проверка делается в строке 20463. После проверки вызов завершает
файловая система, делая процесс лидером группы без управляющего терминала.
В противоположность системным вызовам, рассмотренным к настоящему моменту
в этой главе, вызовы в файле mi sc . с не требуются стандартом POSIX. Их нали
чие обусловлено необходимостью обеспечить взаимодействие с ядром драйверов
устройств и серверов, находящихся в MINIX 3 в пользовательском пространст
ве. Подобная необходимость отсутствует в монолитных операционных системах.
Вызовы перечислены в табл. 4.8.
Резюме
В этой главе были проанализированы как общие принципы управления памя
тью, так и их реализация в MINIX 3. Мы увидели, что в простейших системах
вообще не поддерживается подкачка или замещение страниц. Программа, загру
женная в память, остается там до своего завершения. Некоторые операционные
системы позволяют находиться в памяти одновременно только одному процессу,
в то время как другие поддерживают многозадачность.
Следующим шагом вперед является подкачка (то есть загрузка и выгрузка целых
процессов). Благодаря подкачке система может обрабатывать большее количест
во процессов, чем то, для которого достаточно пространства в памяти. Процессы,
для которых в ней нет места, целиком выгружаются на диск. Свободные области
в памяти и на диске можно отслеживать с помощью битового массива или спи
ска свободных участков.
Современные компьютеры часто поддерживают некоторую форму виртуальной
памяти. В простейшем виде адресное пространство каждого процесса делится на
части постоянного размера, называемые страницами, которые могут размещаться
в любом доступном страничном блоке в памяти. Существует множество алгорит
мов замещения страниц, два наилучших — это алгоритмы «старения» и «второго
шанса». Для хорошей работы систем замещения страниц выбрать хороший алго
ритм недостаточно; необходимо также обратить внимание на такие вопросы, как
определение рабочего набора, стратегию предоставления памяти и размер страниц.
Сегментация помогает в управлении структурами данных, изменяющими свой
размер во время выполнения, и упрощает процессы компоновки и совместного
доступа. Она также облегчает предоставление различных видов защиты разным
сегментам. Иногда механизмы сегментации и замещения страниц комбинируют
ся, что позволяет организовать двухмерную виртуальную память. Системы Intel
Pentium поддерживают сегментацию и замещение страниц памяти.
Управление памятью в MINIX 3 реализовано просто. Процессу выделяется па
мять, когда он делает системный вызов fork или ехес. После того как область
памяти выделена, она никогда не меняет своих размеров, пока работает процесс.
На машинах с процессорами Intel система MINIX 3 поддерживает для процессов
две модели памяти. У маленьких программ инструкции и данные могут разме
щаться в одном сегменте. В более сложных программах адресные пространства
данных и кода могут быть разными. Такие процессы могут иметь общий код,
Вопросы и задания 525
поэтому при вызове fork им выделяется память лишь под данные и стек. Такое
не исключено и при вызове ехес, если окажется, что в памяти уже находится
процесс, использующий тот же код, который нужен новой программе.
По большей части работа менеджера процессов связана не с отслеживанием памя
ти, что делается при помощи списка свободных блоков и алгоритма выбора первого
подходящего блока, а с обслуживанием системных вызовов, относящихся к управ
лению памятью. Некоторые из системных вызовов обеспечивают работу сигналов
в стиле POSIX, а поскольку по умолчанию большинство таких сигналов завершают
процесс, имеет смысл обрабатывать их в менеджере процессов, инициирующем за
вершение всех процессов. Отдельные системные вызовы напрямую к работе с па
мятью не относятся, но также обрабатываются менеджером процессов в основном
из-за того, что он меньше файловой системы по объему и поместить их здесь удобнее.
Вопросы и задания
1. Компьютерная система имеет достаточно места для того, чтобы содержать в опе
ративной памяти четыре программы. Эти программы простаивают в ожида
нии ввода-вывода половину времени. Какая часть времени работы централь
ного процессора пропадает?
2. Рассмотрим систему подкачки, когда в памяти содержатся свободные участки
следующих размеров и в следующем порядке: 10 Кбайт, 4 Кбайт, 20 Кбайт,
18 Кбайт, 7 Кбайт, 9 Кбайт, 12 Кбайт и 15 Кбайт. Какой из них будет выбран
по алгоритму первого соответствия для успешного удовлетворения запроса
сегмента следующего размера:
1) 12 Кбайт;
2) 10 Кбайт;
3) 9 Кбайт.
Ответьте на тот же самый вопрос для алгоритмов наилучшего соответствия,
наихудшего соответствия и следующего соответствия.
3. У компьютера есть 1 Гбайт оперативной памяти, выделяемой блоками по
64 Кбайт. Сколько килобайтов занимает битовая карта, хранящая данные о сво
бодных блоках?
4. Рассмотрите предыдущий вопрос для случая, когда вместо битовой карты ис
пользуется список свободных блоков. Сколько памяти займет список в луч
шем и худшем случаях? Считайте, что операционная система занимает пер
вые 512 Кбайт памяти.
5. В чем разница между физическим адресом и виртуальным?
6. Опираясь на таблицу страниц на рис. 4.8, сосчитайте физический адрес, соот
ветствующий каждому из следующих виртуальных адресов:
1) 20;
2) 4100;
3) 8300.
526 Глава 4. Управление памятью
7. На рис. 4.9 поле страницы виртуального адреса имеет разрядность 4 бита, а по
ле страницы физического адреса — 3 бита. Может ли число страничных битов
виртуального адреса в общем случае быть больше, меньше и равно числу битов
физического адреса? Объясните ответ.
8. Процессор Intel 8086 не поддерживает виртуальную память. Тем не менее не
которые компании ранее продавали системы, содержащие стандартный про
цессор 8086 и выполняющие замещение страниц. Предположите, как они это
делали. Подсказка', подумайте о логическом расположении блока управления
памятью (MMU).
9. Считая, что команда выполняется за 1 мкс, а ошибка отсутствия страницы
требует дополнительно п мкс, напишите выражение для фактического време
ни выполнения команды с учетом того, что ошибки происходят через каждые
k инструкций.
10. Компьютер имеет 32-разрядное адресное пространство и страницы размером
8 Кбайт. Таблица страниц целиком поддерживается аппаратно, на запись в ней
отводится одно 32-разрядное слово. При запуске процесса таблица страниц
копируется из памяти в аппаратуру, одно слово требует 100 нс. Если каждый
процесс работает в течение 100 мс (включая время загрузки таблицы страниц),
какая доля времени процессора жертвуется на загрузку таблицы страниц?
И. Компьютер с 32-разрядным адресом использует двухуровневую таблицу стра
ниц. Виртуальные адреса расщепляются на 9-разрядное поле таблицы верх
него уровня, И-разрядное поле таблицы страниц второго уровня и смещение.
Чему равен размер страниц и сколько их в адресном пространстве?
12. Далее представлен алгоритм фрагмента программы для компьютера с разме
ром страницы 512 байт. Программа расположена по адресу 1020, указатель
стека равен 8192 (стек увеличивается по направлению к нулю). Напишите
последовательность страничных обращений, создаваемую этой программой.
Каждая инструкция занимает 4 байта (1 слово), включая непосредственные
константы. В последовательности обращений учитываются обращения как
к инструкциям, так и к данным.
Загрузить слово 6144 в регистр 0.
Поместить содержимое регистра 0 в стек.
Вызвать процедуру по адресу 5120, поместив в стек адрес возврата.
Вычесть константу 16 из указателя стека.
Сравнить полученный результат с константой 4.
При равенстве перейти на адрес 5152.
13. Предположим, что 32-разрядный виртуальный адрес разбивается на четыре
поля. Первые три используются для трехуровневой системы таблиц страниц.
Четвертое поле — это смещение. Зависит ли количество страниц от размера
всех четырех полей? Если нет, какие из полей имеют значение, а какие нет?
14. Компьютер, процессы которого имеют 1024 страницы в своем адресном про
странстве, хранит таблицы страниц в памяти. На чтение слова из таблицы
Вопросыи задания 527
5.1. Файлы
В следующих нескольких разделах файлы рассматриваются с точки зрения поль
зователя, то есть обсуждаются их использование и свойства.
из записей, важным является то, что операция чтения возвращает одну запись,
а операция записи обновляет или дополняет одну запись. Несколько десяти
летий назад, когда вовсю применялись перфокарты, состоящие из 80 колонок
отверстий, многие операционные системы (на мэйнфреймах) оперировали фай
лами, состоящими из 80-символьных записей — образов перфокарт. Этими опе
рационными системами поддерживались также файлы, составленные из 132-
символьных записей, предназначенные для линейных принтеров (которые в те
дни печатали по 132 символа в строке). В результате программы читали из вход
ных файлов 80-символьные блоки и тут же расширяли их до 132-символьных
блоков. Ни одна современная универсальная система не работает подобным
образом.
1 Байт 1 Запись
Третий вариант файловой структуры показан на рис. 5.1, в. При такой органи
зации файл представляет собой дерево записей, не обязательно одной и той
же длины. Каждая запись содержит ключевое поле в фиксированной позиции.
Дерево упорядочено по ключевому полю с целью быстрого поиска по заданному
ключу.
Основной файловой операцией здесь является не получение следующей записи,
хотя это также возможно, а получение записи с указанным значением ключа.
Для файла, показанного на рис. 5.1, в, можно, например, запросить у системы
запись с ключом «пони», не беспокоясь о точном положении записи в файле
(вольера в зоопарке). При добавлении новой записи операционная система, а не
пользователь должна решать, куда ее поместить. Такой тип файлов принципи
ально отличается от неструктурированных потоков байтов, применяемых в UNIX
и Windows 98. Подобные файловые системы характерны для больших мэйнфрей
мов, еще используемых для коммерческой обработки данных.
5.1. Файлы 535
«Магическое» число
Размер текста
Размер данных
Размер
релокационного блока
Размер таблицы
символов
Точка входа
Флаги
Текст
Данные
Биты релокации
Таблица
символов
пользователь должен для получения доступа к файлу указать пароль. В этом слу
чае пароль должен входить в атрибуты файла.
Флаги представляют собой отдельные биты или короткие битовые поля, управляю
щие некоторыми специфическими свойствами. Например, скрытые файлы не по
являются в перечне файлов при распечатке каталога. Флаг архивации представляет
собой бит, следящий за тем, была ли создана для файла резервная копия. Этот
флаг очищается программой архивирования и устанавливается операционной
системой при изменении файла. Таким образом, программа архивирования мо
жет определить, какие файлы следует архивировать. Временный файл можно ав
томатически удалить по окончании работы создавшего файл процесса.
Атрибуты длины записи, позиции ключа и длины ключа присутствуют только
у тех файлов, записи которых могут искаться по ключу.
Различные атрибуты, хранящие значения времени, позволяют следить за тем,
когда файл был создан, когда в последний раз он был изменен, когда к нему в по
следний раз предоставлялся доступ. Эти сведения можно использовать в различ
ных целях. Например, если исходный файл программы был модифицирован по
сле создания соответствующего ему объектного файла, данный исходный файл
должен быть перекомпилирован.
В качестве текущего размера файла указывается количество байтов в файле в на
стоящий момент. В некоторых старых операционных системах мэйнфреймов
при создании файла требовалось указать также максимальную длину файла, что
позволяло операционной системе зарезервировать достаточно места для его по
следующего увеличения. Современные операционные системы, работающие на
персональных компьютерах, умеют обходиться без подобного резервирования.
Удаление файла. Когда файл уже более не нужен, его удаляют, чтобы освобо
дить пространство на диске. Этот системный вызов поддерживается во всех
операционных системах.
open
Открытие файла. Прежде чем использовать файл, процесс должен его открыть.
Системный вызов open позволяет системе считать в оперативную память ат
рибуты файла и список дисковых адресов для быстрого доступа к содержимо
му файла при последующих вызовах.
540 Глава 5. Файловые системы
close
lock
5.2. Каталоги
В файловых системах файлы обычно организуются в каталоги, или папки, ко
торые, в свою очередь, в большинстве операционных систем также являются
файлами. В данном разделе мы рассмотрим каталоги, их организацию, свойства
и действия, которые могут быть выполнены с ними.
Структура данных,
содержащая атрибуты
Рис. 5.3. Организация каталогов: а — атрибуты хранятся в каталоге;
б — атрибуты хранятся отдельно
Когда открывается файл, операционная система ищет его запись в каталоге. Затем
она извлекает и загружает в память атрибуты и дисковые адреса либо из самой
записи, либо из структуры, на которую запись ссылается. При всех последую
щих обращениях к файлу используется информация из памяти.
Количество каталогов меняется от системы к системе. В простейшем варианте име
ется один каталог, в котором хранятся все файлы всех пользователей (рис. 5.4, а).
Подобные системы были распространены в ранних персональных компьютерах,
отчасти потому, что компьютеры были однопользовательскими.
542 Глава 5. Файловые системы
Рис. 5.4. Три варианта устройства файловой системы: а — один каталог на всех пользователей;
б — отдельный каталог для каждого пользователя; в — дерево каталогов для каждого
из пользователей. Буквами обозначены владельцы каталога или файла
5.2.3. Пути
При организации файловой системы в виде дерева каталогов требуется некий
способ указания файла. Для этого обычно используют два метода. В первом слу
чае обращение к файлу выполняется по абсолютному пути, составленному из
имен всех каталогов от корневого до того, в котором содержится файл, и имени
самого файла. Например, путь /usr/ast/mailbox означает, что корневой ка
талог содержит подкаталог us г, в который, в свою очередь, вложен подкаталог
ast, где находится файл mailbox. Абсолютные пути всегда начинаются от кор
невого каталога и являются уникальными. В UNIX компоненты пути разделяют
ся косой чертой (/). В Windows в качестве разделителя принята обратная косая
черта (\). Таким образом, одно и то же имя пути в этих операционных системах
будет выглядеть следующим образом:
+ Windows:
\usr\ast\mailbox
♦ UNIX:
/usr/ast/mailbox
Относительная форма задания пути часто оказывается более удобной, хотя она
подразумевает то же, что и абсолютная.
Некоторым программам требуется доступ к файлам независимо от того, какой
каталог является в данный момент текущим. В этом случае они всегда должны
указывать абсолютные имена. Например, программе проверки правописания может
понадобиться для выполнения работы прочитать файл /usr/lib/dictionary.
В этом случае она должна использовать абсолютное имя файла, поскольку за
ранее неизвестно, каким будет рабочий каталог при ее вызове. Абсолютное
имя файла будет работать всегда, независимо от того, какой каталог является
текущим.
Если программе проверки правописания понадобится большое количество фай
лов из каталога /usr/lib, она может, обратившись к операционной системе, поме
нять рабочий каталог на /usr/lib, после чего указывать просто имя dictionary
в качестве первого аргумента системного вызова open. Явно указав свой рабо
чий каталог, программа может использовать в дальнейшем относительные име
на, поскольку точно знает, где она находится в дереве каталогов.
У каждого процесса есть свой рабочий каталог, поэтому, когда процесс меняет
свой рабочий каталог и потом завершает работу, это не влияет на работу других
процессов, и в файловой системе не остается никаких следов от подобных изме
нений. Таким образом, процесс может без опасений менять свой рабочий ката
лог, когда это ему удобно. С другой стороны, если библиотечная процедура по
меняет свой рабочий каталог и не восстановит его при возврате управления,
программа, вызвавшая такую процедуру, может оказаться не в состоянии про
должать свою работу, так как ее предположения о текущем каталоге окажутся
неверными. По этой причине библиотечные процедуры редко меняют рабочие
каталоги, а когда все-таки меняют, обязательно восстанавливают старое имя пе
ред возвратом управления.
Большинство операционных систем, поддерживающих иерархические катало
ги, имеют специальные записи в каждом каталоге, означающие текущий (.)
и родительский (. .) каталог. Чтобы продемонстрировать, как работает по
добная запись, обратимся к дереву каталогов системы UNIX, показанному на
рис. 5.5.
5.2. Каталоги 545
Две точки являются инструкцией системе подняться вверх (в каталог usr). По
сле этого нужно открыть каталог lib и найти в нем файл dictionary.
Второй вариант обозначения (.) указывает на текущий каталог. Когда команда
ср получает имя каталога (включая точку) в качестве последнего аргумента, она
копирует в него все файлы. Разумеется, более адекватным способом копирова
ния является команда
ср usr/lib/dictionary .
Удаление каталога. Может быть удален только пустой каталог. Записи точка (.)
и две точки (. .) файлами не являются и удалены быть не могут.
opendir
Открытие каталога. После этой операции каталог может быть прочитан. На
пример, для распечатки всех файлов каталога программа, создающая список,
открывает каталог, чтобы прочитать имена всех содержащихся в нем файлов.
Прежде чем каталог может быть прочитан, его следует открыть, подобно от
крытию и чтению файла.
closedir
Закрытие каталога. Когда каталог прочитан, его следует закрыть, чтобы осво
бодить место во внутренней таблице системы.
readdir
unlink
Диск целиком
Неразрывные файлы
Простейшей схемой выделения файлам определенных блоков на диске являет
ся система, в которой файлы представляют собой наборы смежных блоков диска.
Тогда на диске с блоками по 1 Кбайт файл размером в 50 Кбайт будет занимать
50 последовательных блоков. У неразрывных файлов есть два существенных пре
имущества. Во-первых, такую модель легко реализовать, так как системе, чтобы
определить, какие блоки принадлежат тому или иному файлу, нужно следить всего
лишь за двумя числами: номером первого блока файла и числом блоков в файле.
Зная первый блок файла, любой другой его блок легко получить при помощи
простой операции сложения.
Во-вторых, при работе с неразрывными файлами производительность просто
превосходна, так как весь файл может быть прочитан с диска за одну операцию.
Требуется только одна операция позиционирования (для первого блока). После
этого более не нужно искать цилиндры и тратить время на ожидание поворота
диска, поэтому данные могут считываться с максимальной скоростью, на какую
способен диск. Таким образом, непрерывные файлы легко реализуются и для
них характерна высокая производительность.
К сожалению, неразрывные файлы имеют и серьезный недостаток: их использо
вание ведет к фрагментации дисков. Поначалу фрагментация не является про
блемой, поскольку каждый новый файл можно записать сразу следом за преды
дущим. Однако в конечном счете диск заполнится, и возникнет необходимость
550 Глава 5. Файловые системы
Связанные списки
Второй метод размещения файлов состоит в представлении каждого файла в ви
де связанного списка блоков диска (рис. 5.7). Первое слово каждого блока явля
ется указателем на следующий блок. В остальной части блока хранятся данные.
Файл А
Неиспользуемый блок
При такой организации все блоки доступны для данных. Кроме того, значитель
но упрощается произвольный доступ. Хотя для обращения к какому-либо блоку
файла все равно понадобится проследовать по цепочке всех ссылок вплоть до
требуемого блока, в данном случае вся цепочка ссылок уже хранится в памяти и
ее прохождение не требует дополнительных дисковых операций. Как и в преды
дущем случае, для доступа ко всем частям файла в каталоге достаточно хранить
один целый индекс (номер начального блока файла).
Основной недостаток этого метода состоит в том, что вся таблица должна по
стоянно находиться в памяти. Для 20-гигабайтного диска с блоками размером
1 Кбайт потребовалась бы таблица из 20 000 000 записей, по одной для каждого
из 20 000 000 блоков диска. Каждая запись должна состоять как минимум из
3 байт. Для ускорения поиска размер записей должен быть увеличен до 4 байт.
Таким образом, резидентная таблица будет занимать 60 или 80 Мбайт опера
тивной памяти. Таблица, конечно, может быть размещена в виртуальной памяти,
но и в этом случае она продолжит занимать большой объем виртуальной па
мяти и дискового пространства, а кроме того, приведет к генерации страничного
трафика. Операционные системы MS-DOS и Windows 98 используют только
файловые системы FAT; более поздние версии Windows также предоставляют
их поддержку.
Индексные узлы
Последний метод соотнесения блоков диска файлам заключается в связывании
с каждым файлом структуры данных, называемой индексным узлом (index node),
или i-узлом (i-node), содержащей атрибуты файла и адреса блоков файла. Про
стой пример индексного узла показан на рис. 5.9. При наличии индексного узла
можно найти все блоки файла. Большое преимущество такой схемы перед хра
нящейся в памяти таблицей из списков состоит в том, что индексный узел ока
зывается в памяти только тогда, когда открыт соответствующий ему файл. Если
каждый индексный узел занимает п байт, а одновременно открыть можно k фай
лов, для массива индексных узлов в памяти потребуется всего kn байт.
Обычно эта величина значительно меньше, чем размер таблицы FAT. Это легко
объясняется. Размер таблицы, хранящей список всех блоков диска, пропорцио
нален емкости самого диска. Для диска из п блоков потребуется п записей в таб
лице. Таким образом, размер таблицы линейно растет с ростом размера диска.
Для схемы индексных узлов, напротив, требуется массив в памяти с разме
ром, пропорциональным максимальному количеству файлов, которые можно
открыть одновременно. При этом не важно, какой именно размер диска, 1, 10
или 100 Гбайт.
С такой схемой связана проблема, суть которой в том, что при выделении каждо
му файлу фиксированного количества дисковых адресов этого количества может
не хватить. Одно из решений заключается в резервировании последнего дис
кового адреса не для блока данных, а для адреса косвенного блока, содержащего
адреса блоков диска. Этот принцип можно расширить и ввести блоки с двойным
и тройным уровнем косвенности, как показано на рис. 5.9.
5.3. Реализация файловой системы 553
С этой проблемой тесно связан вопрос хранения атрибутов файла. Каждая фай
ловая система поддерживает различные атрибуты файла, такие как дату созда
ния файла, имя владельца и т. д., и всю эту информацию нужно где-то хранить.
Один из очевидных вариантов — поместить эти сведения непосредственно в за
пись каталога. В простейшем случае каталог представляет собой список записей
фиксированного размера, по одной записи на файл, содержащих имя файла фик
сированной длины, структуру атрибутов и один или несколько дисковых адре
сов (не более определенного максимума), определяющих расположение блоков,
как мы видели на рис. 5.3, а.
Системы с индексными узлами могут хранить атрибуты в индексных узлах, а не
в записях каталога, как на рис. 5.3, б. В этом случае запись каталога короче: она
содержит только имя файла и номер индексного узла.
Общие файлы
В главе 1 мы кратко упомянули о связях между файлами, например, одного проекта,
упрощающих совместную работу с ними нескольких пользователей. На рис. 5.10
показана файловая система с рис. 5.4, в, однако теперь один из файлов пользова
теля С присутствует также в одном из каталогов пользователя В.
Общий файл
Рис. 5.10. Файловая система, содержащая общий файл
на индексный узел другой файловой системы. Кроме того, файл может иметь
только одного владельца и один набор разрешений. Если владелец совместно ис
пользуемого файла удалит его запись из своего каталога, не исключено, что дру
гой пользователь лишится возможности удалить этот файл из своего каталога
(при отсутствии соответствующего разрешения).
Альтернативным способом совместного использования файлов является созда
ние нового типа файла, содержимое которое представляет собой путь к другому
файлу. Такой вид связи работает для монтируемых файловых систем. Более то
го, если в путях существует возможность указывать сетевые адреса, можно орга
низовать связь с файлом, расположенным на другом компьютере. В UNIX этот
вид связи называется символьной связью, в Windows — ярлыком, а в Mac OS фир
мы Apple — псевдонимом. Символьные связи могут применяться в системах, где
атрибуты хранятся в записях каталогов. Несложно понять, что синхронизация
множества записей каталогов, которые содержат атрибуты, — непростая задача.
Любое изменение, внесенное в файл, затрагивает все соответствующие ему за
писи каталогов. Недостатком символьных связей является то, что при удалении
и даже при переименовании целевого файла они становятся недействительными.
Каталоги в Windows 98
Файловая система в начальной редакции Windows 95 была идентична файловой
системе MS-DOS, однако уже во второй редакции была организована поддержка
длинных имен файлов и файлов большего объема. Мы будем ссылаться на вторую
версию файловой системы как на файловую систему Windows 98, хотя ее можно
найти и на некоторых компьютерах, работающих под управлением Windows 95.
В Windows 98 поддерживаются два типа записей каталогов; первую из них
(рис. 5.11) мы будем называть базовой записью.
Байты 831114 2 2 4 24
Дата/время Последний Дата/время Размер
Базовое имя Расш. NT создания доступ последней записи файла
Атрибуты^
Десятые доли Старшие 16 бит
f Младшие 16 бит
f
времени создания начального блока начального блока
Рис. 5.11. Базовая запись каталога в Windows 98
Байты 1 10 111 12 2 4
5 символов 6 символов 2 символа
Если все это кажется сложным, мы, пожалуй, согласимся с вами. Обеспечение
обратной совместимости с более ранними и простыми системами в сочетании с
новыми возможностями приводит к хаосу. Несомненно, борцы за чистоту идей
выступят против хаоса, но едва ли смогут разбогатеть на новых версиях операци
онных систем.
Каталоги в UNIX
В UNIX применяется исключительно простая структура каталогов (рис. 5.13).
Здесь каждая запись состоит из имени файла и номера индексного узла. Вся ос
тальная информация, о размере файла, его типе, владельцах, времени изменения
и занимаемых им дисковых блоках, хранится в индексном узле. В некоторых
UNIX-подобных системах применяется другая схема, но, в любом случае, за
пись каталога состоит исключительно из ASCII-строки и номера индексного узла.
Байты 2 14
Имя файла
Т--------------------------------------------
Номер
i-узла
Рис. 5.13. Запись каталога в UNIX версии 7
1-узел 26
1-узел 6 содержит
содержит Блок 132 данные Блок 406
данные содержит о каталоге содержит
Корневой каталог о каталоге /usr каталог 132 /usr/ast каталог /usr/ast
1 • Режимный код
6 • 26 •
1 • • Размер 1 • • 6 • •
Времена
4 bin 19 dick 64 grants
7 dev 132 30 erik 92 books
14 lib 51 jim 60 mbox
9 ect 26 ast 81 minix
6 usr 45 bal 17 src
8 tmp
Каталоги в NTFS
Текущей файловой системой для продуктов Microsoft на сегодняшний день явля
ется NTFS (New Technology File System — файловая система новой технологии).
Рамки этой книги не предусматривают ее детального описания, однако некото
рые проблемы, с которыми сталкивается NTFS, и их решениями мы ознакомимся.
Одна из проблем — длинные имена файлов и путей. NTFS поддерживает длин
ные имена файлов (до 255 символов) и путей (до 32 767 символов). Поскольку
предшествующие версии Windows в любом случае не способны читать файло
вую систему NTFS, сложная структура каталогов с обратной совместимостью не
нужна, и поле имени имеет переменную длину. Также предоставляется поддерж
ка второго имени в формате 8 + 3, позволяющая устаревшим системам получать
доступ к NTFS-файлам по сети.
NTFS предусматривает использование в именах файлов различных алфавитов
с помощью кодировки Unicode. В Unicode каждый символ занимает 16 бит; это
го достаточно для представления множества языков с очень большими алфави
тами (например, японского языка). Однако помимо представления алфавитов,
многоязычности присущи и другие проблемы. Даже среди языков, использующих
латиницу, имеются свои тонкости. Так, в некоторых языках (к примеру, в испан
ском) определенные комбинации двух символов при сортировке считаются од
ним символом. Слова, начинающиеся с префиксов «ch» и «11», должны следовать
после слов, начинающихся соответственно с префиксов «cz» и «lz». Еще сложнее
проблема чувствительности к регистру. Если по умолчанию имена файлов чув
ствительны к регистру, иногда может возникать необходимость в организации
нечувствительного к регистру поиска. Для языков на основе латиницы решение
проблемы очевидно, по крайней мере, их носителям. Если поддерживается только
один язык, правила очевидны, однако Unicode позволяет смешивать различные
языки. В многонациональной организации один и тот же каталог может содер
жать имена файлов на греческом, русском и японском языках. В качестве реше
ния проблемы в NTFS введен атрибут файла, определяющий соглашения о реги
стре для языка, на котором написано его имя файла.
С помощью дополнительных атрибутов в NTFS решено много задач. Если в UNIX
файл представляет собой последовательность байтов, то в NTFS — коллекцию
атрибутов, где каждый атрибут является потоком байтов. Базовая структура дан
ных NTFS — главная таблица файлов (Master File Table, MFT). Она поддержи
вает 16 атрибутов, каждый из которых может иметь длину до 1 Кбайт. Если этого
недостаточно, атрибут можно использовать в качестве заголовка, указывающего
на дополнительный файл с расширенными значениями атрибута. Такой атрибут
называется нерезидентным. Сама таблица MFT представляет собой файл и со
держит запись для каждого файла и каталога файловой системы. Поскольку ее
объем может значительно вырасти, при создании NTFS около 12,5 % пространст
ва раздела резервируется под MFT. Благодаря резервированию MFT не фраг
ментируется как минимум до тех пор, пока все зарезервированное пространст
во не будет исчерпано. В последнем случае для MFT резервируется еще одна
5.3. Реализация файловой системы 559
область. Таким образом, даже если таблица MFT фрагментирована, она состоит
из очень небольшого числа крупных блоков.
Как же в NTFS обстоит дело с данными? Данные попросту представляют собой
один из атрибутов файла. На самом деле, NTFS-файл может содержать несколь
ко потоков данных. Изначально эта возможность позволяла Windows-серверам
обслуживать файлы клиентов Apple Macintosh. В исходной операционной систе
ме Macintosh (до Mac OS 9) все файлы имели два потока данных. Эти потоки
назывались «ветвь ресурсов» и «ветвь данных». Множественные потоки данных
имеют и другие применения; например, для большого графического файла можно
хранить его уменьшенный эскиз. Максимальный объем потока составляет 264 бай
та. В то же время система NTFS способна хранить содержимое небольших файлов
(до нескольких сотен байтов) в заголовке атрибута. Такие файлы называются
непосредственными [91].
Мы лишь слегка затронули несколько подходов, позволяющих NTFS решать про
блемы, не решенные более старыми и простыми файловыми системами. NTFS
также предоставляет и другие возможности: сложную систему защиты, шифро
вание и сжатие данных. Их описание, как и описание их реализации, занимает
гораздо больше места, чем мы можем позволить себе в этой книге. Более деталь
ное рассмотрение NTFS вы найдете в [115]. Кроме того, дополнительную инфор
мацию можно поискать в Интернете.
Размер блока
После принятия решения о хранении файлов блоками фиксированного размера
возникает вопрос о размере блоков. Учитывая организацию дисков, очевидными
кандидатами на роль блоков являются сектор, дорожка и цилиндр диска (недос
татком такого выбора является зависимость этих параметров от устройств). В сис
теме управления страницами памяти страницы также входят в число основных
560 Глава 5. Файловые системы
кандидатов. Если выбрать большую единицу хранения, такую как цилиндр, это
будет означать, что любой файл, даже состоящий из одного байта, займет как ми
нимум целый цилиндр.
В то же время при маленьких единицах хранения каждый файл будет состоять
из большого числа блоков. Для чтения каждого блока файла обычно требуется
операция поиска нужного цилиндра и ожидание поворота диска, поэтому чтение
файла, состоящего из большого числа блоков, окажется медленным.
Например, представьте себе диск, в котором каждая дорожка содержит 131 072 байт
(128 Кбайт), период вращения составляет 8,33 мс, а среднее время поиска — 10 мс.
При этом время, требующееся для чтения блока из k байт, равно сумме времен
поиска, поворота и переноса данных:
10+ 4,165 + (V131 072) х 8,33.
Сплошная кривая на рис. 5.15 показывает зависимость скорости передачи дан
ных от размера блока.
Рис, 5,15, Зависимость скорости чтения/записи данных диска (сплошная линия, левая
шкала) и эффективности использования дискового пространства (штриховая линия,
правая шкала) от размера блоков. Все файлы по 2 Кбайт
1 052 689 блоков, чтобы охватить все 228 дисковых блока. Часто список свобод
ных блоков хранится в самих свободных блоках.
1001101101101100
0110110111110111
1010110110110110
0110110110111011
1110111011101111
1101101010001111
0000111011010111
1011101101101111
1100100011101111
0111011101110111
1101111101110111
Резервные копии
Большинство пользователей считают создание резервных копий файлов просто
потерей времени. Однако когда в один прекрасный день диск внезапно отказы
вается работать, они диаметрально меняют свои привычки. Компании, напротив,
обычно хорошо осознают ценность своей информации и выполняют резервное
копирование один раз в сутки, чаще всего на магнитную ленту. Современные
магнитные ленты вмещают десятки и иногда даже сотни гигабайтов при стоимо
сти в несколько центов за гигабайт. Однако создание резервных копий является
далеко не столь тривиальным делом, как это может показаться, поэтому мы кос
немся некоторых аспектов данной темы.
Как правило, резервирование на магнитную ленту осуществляется для решения
одной из двух потенциальных проблем: восстановления после аварии и вос
становления после глупой ошибки. В первом случае требуется вернуть компью
тер в работоспособное состояние после отказа диска, пожара, потопа или другой
природной катастрофы. На практике такие вещи происходят довольно редко,
и именно поэтому многие пользователи не заботятся о резервировании. Как
правило, в силу той же причины они не страхуют свое имущество от пожара.
Второй случай наступает тогда, когда пользователи случайно удаляют нужные
им файлы. Это происходит настолько часто, что разработчики Windows придума
ли специальный каталог, называемый корзиной (recycle bin), куда на самом деле
перемещается удаляемый файл и где он впоследствии может быть легко обнаружен
и восстановлен. Резервные копии являются развитием этого принципа и позво
ляют восстанавливать файлы, удаленные несколько дней или даже недель назад.
Создание резервной копии отнимает много времени и требует большого про
странства, поэтому важно, чтобы этот процесс был эффективным и удобным.
Возникает ряд вопросов, и первый из них — следует резервировать файловую
систему целиком или лишь ее часть? Зачастую исполняемые (двоичные) про
граммы содержатся в ограниченной части дерева файловой системы; резервиро
вать их не обязательно, поскольку такие файлы можно восстановить с компакт-
дисков производителей. Кроме того, большинство систем имеют каталог времен
ных файлов, и резервировать его также не имеет смысла. В UNIX все специаль
ные файлы (устройства ввода-вывода) содержатся в каталоге /dev/. Резерви
рование этого каталога не только не обязательно, а попросту опасно, поскольку
программа резервирования при попытке считывания файлов зависнет. Короче
говоря, чаще всего предпочтительнее создавать резервные копии содержимого
отдельных каталогов, нежели копировать всю файловую систему.
5.3. Реализация файловой системы 565
Номер блока
0123456789 101112131415
Занятые
1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 блоки
Свободные
0 0 1 0 1 0 0 0 0 1 1 0 0 0 1 1 блоки
Номер блока
0123456789 101112131415
Занятые
1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 блоки
Свободные
0 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 блоки
б
0 1 2 3 4 5 6 7 8 9 101112131415
i| i|o| l|o| l| i| i| 1I0I011| i| 1I0I0 Занятые
блоки
0 0 1 0 2 0 0 0 0 1 1 0 0 0 1 1 Свободные
блоки
в
Кэширование
Для минимизации количества обращений к диску применяется блочный, или бу
ферный, кэш. (Термин «кэш» происходит от французского слова cacher, что зна
чит «скрывать».) В данном контексте кэшем называется набор блоков, логически
принадлежащих диску, но хранящихся в оперативной памяти по соображениям
производительности.
Существуют различные алгоритмы кэширования. Обычная практика заключается
в перехвате всех запросов чтения к диску и поиске требующихся блоков в кэше.
Если блок присутствует в кэше, то запрос чтения блока может быть удовлетворен
572 Глава 5. Файловые системы
Более того, к некоторым блокам, таким как блоки индексных узлов, программы
редко обращаются дважды в течение короткого интервала времени. Исходя из
этих соображений, мы приходим к модифицированной схеме LRU, принимая во
внимание два вопроса.
1. Насколько велика вероятность того, что данный блок скоро снова понадобится?
2. Важен ли данный блок для непротиворечивости файловой системы?
Для ответа на каждый из поставленных вопросов блоки можно разделить на ка
тегории, такие как блоки индексных узлов, «косвенные» блоки (блоки косвен
ной адресации), блоки каталогов, блоки, целиком заполненные данными, и бло
ки, частично заполненные данными. Блоки, которые, вероятно, не потребуются
снова в ближайшее время, помещаются в начало LRU-списка, чтобы занимаемые
ими буферы могли вскоре освободиться. Блоки, вероятность повторного исполь
зования которых в ближайшее время высока (например, частично заполненные
записываемые блоки), заносятся в конец LRU-списка, что позволяет им дольше
оставаться в кэше.
Второй вопрос не связан с первым. Если блок представляет важность для непро
тиворечивости файловой системы (обычно это все блоки, кроме блоков данных)
и такой блок модифицируется, то его следует немедленно сохранить на диске,
независимо от его положения в LRU-списке. Своевременно записывая критиче
ски важные блоки, мы значительно снижаем вероятность того, что сбой компью
тера повредит файловую систему. Пользователь вряд ли будет рад потере одного
из своих файлов из-за какого-то сбоя. Еще сильнее он огорчится, если при этом
испорченной окажется вся файловая система.
Даже при принятии всех перечисленных мер предосторожности по поддержанию
в рабочем состоянии файловой системы слишком долгое хранение в кэше блоков
с данными является нежелательным. Представьте себе автора будущей книги,
подготавливаемой на персональном компьютере. Даже если наш писатель перио
дически велит текстовому редактору сохранять редактируемый файл на диске,
есть большая вероятность, что все блоки останутся в кэше. Если произойдет сбой,
структура файловой системы не пострадает, но труд целого дня будет потерян.
Эта ситуация случается не слишком часто, и если случается, то только с очень
невезучими пользователями. Для решения данной проблемы обычно применяет
ся два метода. В системе UNIX есть вызов sync, принуждающий сохранить все
модифицированные блоки кэша на диске. При загрузке операционной системы
запускается фоновая программа, обычно под названием update, вся работа
которой заключается в периодическом (обычно через каждые 30 с) обращении
к системному вызову sync. В результате при любом сбое будет потеряно не бо
лее полминуты работы.
В Windows практикуется другой подход, состоящий в том, что каждый модифи
цированный блок записывается на диск сразу же. Кэш, в котором все модифици
рованные блоки немедленно записываются на диск, называется сквозным кэшем,
или кэшем со сквозной записью. При использовании сквозного кэша количество
обращений ввода-вывода к диску больше, чем при применении обычного кэша.
Чтобы лучше понять разницу в этих двух подходах, представьте себе программу,
574 Глава 5. Файловые системы
а б
Рис. 5.19. Уменьшение количества перемещений головки диска: а — индексные узлы,
размещенные в начале диска; б — диск, разделенный на группы цилиндров, каждая
с собственными блоками и индексными узлами
576 Глава 5. Файловые системы
5.4. Безопасность
Многие компании располагают ценной информацией, которую они тщательно
охраняют. Таким образом, защита информации от несанкционированного досту
па является главной заботой всех операционных систем. В следующих разделах
мы рассмотрим различные вопросы, связанные с безопасностью и защитой, рав
но относящиеся как к системам разделения времени, так и к персональным ком
пьютерам, связанным локальной сетью.
Угрозы
С точки зрения безопасности компьютерные системы ставят три общие цели, ка
ждой из которых соответствует своя категория угроз (табл. 5.3). Первая, конфи
денциальность данных, подразумевает, что данные, не подлежащие разглашению,
остаются тайными. Говоря точнее, если владелец данных разрешил доступ к ним
лишь ограниченному кругу лиц, то система должна гарантировать невозможность
несанкционированного доступа к ней. Как минимум, пользователь должен иметь
возможность указывать, кому и что разрешено просматривать, а система обязана
претворять эти указания в жизнь.
Злоумышленники
Большинство людей совершенно безвредно и законопослушно. Так зачем же бес
покоиться о безопасности? Затем, что есть небольшая группа людей, которая от
нюдь не безвредна и жаждет причинять неприятности другим (возможно, для
собственной коммерческой выгоды). В литературе по безопасности злоумышленни
ком называют человека, сующего свой нос в чужие дела. Злоумышленники подраз
деляются на два вида. Пассивные злоумышленники просто пытаются прочитать
файлы, которые им не разрешено читать. Активные злоумышленники пытаются
незаконно изменить данные. При разработке системы защиты от злоумышлен
ников важно знать врага, против которого нужно возводить укрепления, в лицо.
Рассмотрим наиболее распространенные категории злоумышленников.
♦ Случайные любопытные пользователи, не применяющие специальных тех
нических средств. У многих людей есть компьютеры, соединенные с общим
файловым сервером. И если не установить специальной защиты, благодаря
естественному любопытству многие начнут читать чужую электронную почту
и другие файлы. Например, во многих системах UNIX новые только что со
зданные файлы по умолчанию доступны для чтения всем желающим.
♦ Любители совать нос в чужие дела. Студенты, системные программисты, опе
раторы и другой технический персонал часто считают взлом системы без
опасности локальной компьютерной системы святым долгом. Как правило,
они имеют высокую квалификацию и готовы посвящать достижению постав
ленной перед собой цели массу времени.
+ Желающие делать деньги любыми средствами. Некоторые программисты, рабо
тающие в банках, предпринимали попытки украсть деньги у банка, в котором
они работали. Их схемы варьировались от изменения способов округления
сумм в программах и сбора, таким образом, «с миру по нитке», до шантажа
(«заплатите мне, или я уничтожу всю банковскую информацию»).
♦ Лица, занимающиеся коммерческим и военным шпионажем. Шпионаж пред
ставляет собой серьезную и хорошо финансированную попытку конкурента
или другой страны украсть программы, коммерческие тайны, ценные идеи
и технологии, чертежи микросхем, бизнес-планы и т. д. Часто такие попытки
подразумевают подключение к линиям связи или установку антенн, направ
ленных на компьютер для улавливания его электромагнитного излучения.
Очевидно, что попытка предотвратить кражу военных секретов враждебным
иностранным государством отличается от противостояния шалостям студентов,
встраивающих забавные сообщения в систему. Необходимые для поддержания
безопасности и защиты усилия зависят от предполагаемого противника.
Вредоносные программы
Еще одну угрозу безопасности представляют собой вредоносные программы.
В определенном смысле автора программы следует считать злоумышленником,
зачастую обладающим хорошо развитыми техническими навыками. Различие ме
жду традиционным злоумышленником и вредоносной программой заключается
5.4. Безопасность 581
Пароли
В наиболее широко применяемой форме аутентификации пользователю предла
гается ввести имя и пароль. Парольная защита легко реализуется. В UNIX это
происходит следующим образом: программа входа в систему просит пользовате
ля ввести имя и пароль, который немедленно шифруется. Затем программа чита
ет файл паролей, представляющий собой последовательность ASCII-строк, где
каждая строка соответствует одному пользователю. Когда нужный пользователь
найден, введенный шифрованный пароль сравнивается с шифрованным паролем
из файла. В зависимости от результата сопоставления пользователю выдается
разрешение или отказ в доступе.
Парольная аутентификация легко уязвима. Зачастую можно видеть публикации
о том, как школьники старших и даже средних классов с домашних компьютеров
взламывают сверхсекретные системы крупных корпораций и правительственных
организаций. Почти всегда взлом происходит вследствие угадывания комбина
ции имени пользователя и пароля.
Несмотря на более свежие исследования, классический труд по вопросу безопас
ности паролей был написан аж в 1979 году на основе исследований систем UNIX
[90]. Авторы скомпилировали список вероятных паролей: имена и фамилии, на
звания улиц, городов, слова из словарей среднего размера (в частности, написан
ные задом наперед), автомобильные номера и короткие строки случайных сим
волов. Затем они сравнили свой полученный таким образом список с системным
файлом паролей, чтобы посмотреть, есть ли совпадения. Как выяснилось, более
86 % от общего количества паролей в файле оказались в их списке.
Если бы все пароли состояли из 7 символов, случайным образом выбранных из
95 печатных символов набора ASCII, их количество было бы равно 957, что при
мерно равно 7 х 1013. Если выполнять 2000 операций шифрования в секунду, на
построение списка для сверки пароля потребуется около 2000 лет. Более того,
под хранение такого списка ушло бы 20 млн магнитных лент. Даже требование
того, чтобы пароли содержали как минимум один символ нижнего регистра, один
символ верхнего регистра, один специальный символ и имели бы как минимум
семь или восемь символов в длину, заметно улучшает ситуацию по сравнению
с тем случаем, когда пароль выбирается пользователем без всяких ограничений.
Даже для случая, когда по политическим причинам невозможно заставить поль
зователей выбирать разумные пароли, в [90] описана технология, делающая пред
лагаемый авторами метод атаки (заранее зашифровать большое число паролей)
практически бесполезным. Идея состоит в том, чтобы ассоциировать с каждым
5.4. Безопасность 587
паролем n-разрядное случайное число. Это случайное число меняется при каж
дом изменении пароля. Оно хранится в файле паролей в незашифрованном виде,
и каждый может его видеть. Пароль же сначала объединяется со случайным чис
лом, а только затем шифруется и записывается в файл.
Рассмотрим, к каким последствиям приведет эта техника для злоумышленника,
пытающегося составить список вероятных паролей, зашифровать их и сохранить
в отсортированном виде в файле f, где их затем легко будет найти. Если злоумыш
ленник считает, что слово Marilyn может быть паролем, ему теперь недостаточно
закодировать только это слово и записать его в f. Он должен зашифровать и за
писать 2п строк, MarilynOOOO, MarilynOOOl, Marilyn0002 и т. д. Благодаря
этой технике, называемой добавлением соли в файл паролей, размер файла f уве
личивается в 2п раз. В UNIX применяется значение п, равное 12. В некоторых
версиях UNIX сам файл паролей сделан недоступным для чтения, а для работы
с ним предоставлена программа, которая просматривает запрошенные записи,
внося дополнительную задержку, достаточную, чтобы сильно замедлить атаку.
Добавление случайных чисел к файлу паролей защищает систему от взломщи
ков, пытающихся заранее составить большой список зашифрованных паролей
и таким образом взломать несколько паролей сразу. Однако данный метод бесси
лен помочь в том случае, когда пароль легко отгадать, например, если пользователь
David использует пароль David. Взломщик может просто попытаться отгадать
пароли один за другим. Обучение пользователей в данной области помогает, но
оно редко проводится. Однако помимо обучения пользователей в вашем распо
ряжении помощь компьютера. На некоторых системах устанавливается програм
ма, формирующая случайные, легко произносимые бессмысленные слова, такие
как fotally, garbungy или bipitty, подходящие в качестве паролей (жела
тельно с чередованием прописных и строчных букв и с разбавлением специаль
ными символами).
Некоторые операционные системы требуют от пользователей регулярной смены
паролей, чтобы ограничить ущерб в ситуации, когда пароль становится извест
ным взломщику. Крайность здесь — одноразовые пароли. В этом случае пользо
ватель получает блокнот, содержащий список паролей. Для каждого входа в сис
тему используется следующий пароль в списке. В итоге, если даже взломщику
удастся узнать уже отработавший пароль, он ему не пригодится. (Предполагает
ся, что пользователь не потеряет выданный ему блокнот.)
Еще одна вариация на тему паролей предполагает, что для каждого нового поль
зователя создается длинный список вопросов и ответов, который хранится на
сервере в надежном виде (например, в зашифрованном). Вопросы должны выби
раться так, чтобы пользователю не нужно было их записывать. Примеры:
+ Как зовут сестру Марджолин?
♦ На какой улице расположена ваша начальная школа?
♦ Что преподавала мисс Воробьофф?
При регистрации сервер задает один из этих вопросов, выбирая его в списке слу
чайным образом, и проверяет ответ.
588 Глава 5. Файловые системы
Физическая идентификация
Совершенно иным подходом к авторизации является проверка наличия у поль
зователя некоторого предмета, как правило, пластиковой карты с нанесенной
магнитной полосой. Обычно пользователь должен не только вставить карту, но
и ввести пароль; таким образом, доступ к системе обеспечивается при соблюде
нии двух условий — наличия у пользователя карты и знания им пароля. Обычно
на этом принципе основана работа банкоматов.
Другой метод проверки подлинности пользователя основан на измерении его
физических характеристик, которые трудно подделать. Например, для иденти
фикации пользователя может применяться специальное устройство считывания
отпечатков пальцев или распознавания тембра голоса. Поиск можно значительно
ускорить, если пользователь при этом будет сообщать системе, кто он; в этом
случае системе будет достаточно выполнить сравнение для одного отпечатка паль
цев, нежели искать предоставленный отпечаток во всей базе данных. Визуальная
идентификация пока не встречается, но со временем и она может появиться.
Еще один метод идентификации заключается в анализе подписи. Пользователь
ставит подпись специальным пером, соединенным с терминалом, и компьютер
сверяет ее с оригиналом. Еще лучше сравнивать не подпись, а движения, выпол
няемые при ее написании. Хороший специалист по подделке подписей может на
рисовать довольно точную копию подписи, но не обязательно угадает, в каком
порядке выполняются движения.
На удивление часто на практике используется измерение длины пальцев. При
этом каждый терминал оснащается устройством вроде показанного на рис. 5.20.
Пользователь засовывает в него руку, и устройство измеряет длину его пальцев
и сравнивает с информацией, хранящейся в базе данных.
Подобные примеры измерения биометрических характеристик можно приводить
еще и еще, но мы остановимся только на двух, которые помогут отметить кое-что
важное. Кошки и другие животные мочой метят свои владения по периметру.
Очевидно, кошки могут идентифицировать друг друга подобным образом. Пред
ставьте себе, что кому-нибудь удастся создать небольшое устройство, способное
производить мгновенный анализ мочи, обеспечивая, таким образом, надежную
идентификацию. Раз так, значит, каждый терминал можно снабдить подобным
устройством, вывесив табличку: «Для регистрации помочитесь сюда, пожалуй
ста». Возможно, таким образом удалось бы создать абсолютно надежную систе
му, хотя она вряд ли понравилась бы пользователям. То же самое можно сказать
о системе, состоящей из иголки и небольшого спектрометра и предлагающей поль
зователю проколоть палец иголкой, предоставив таким образом каплю крови для
анализа. Дело в том, что любая схема аутентификации должна быть психологи-
5.4. Безопасность 589
Контрмеры
В некоторых компьютерных системах, серьезно продуманных в плане безопасно-
сти (отношение к этому вопросу, как правило, кардинально меняется на следую
щий день после того, как злоумышленник вломился в систему и причинил серьез
ный ущерб), часто предпринимаются шаги, призванные максимально усложнить
неавторизованный вход в систему. Например, пользователям может быть разре
шено входить в систему только со специального терминала и только в опреде
ленные дни недели и часы.
Коммутируемые телефонные линии также можно сделать более безопасными.
Например, всем можно разрешить регистрироваться в системе через модем по
телефонной линии, но после успешной регистрации система немедленно прервет
соединение и сама позвонит пользователю по заранее условленному номеру.
Такая мера означает, что взломщик не вломится в систему с любой телефонной
линии. Для работы в системе подойдут только линии зарегистрированных поль
зователей. В любом случае, с применением данной техники или без нее система
обязана выдерживать паузу, по крайней мере, в 10 с при проверке пароля и увели
чивать этот временной интервал после каждой неуспешной регистрации, чтобы
снизить частоту попыток взломщика. После трех неуспешных попыток регистра
ции линия должна отключаться на 10 мин, а персонал уведомляться о попытке
несанкционированного входа в систему.
590 Глава 5. Файловые системы
Объект
Чтение
Чтение
Запись
Чтение
Чтение
Чтение Запись Запись
Запись
Исполнение
Чтение
Запись Запись Запись
Исполнение
Переключение между доменами также легко реализуется при помощи все той же
матрицы, если считать домены объектами, над которыми разрешена операция enter
(вход). На рис. 5.23 снова изображена та же матрица, что и на предыдущем рисун
ке, но с тремя доменами, выступающими и в роли объектов. Процессы в состоянии
переключаться с домена 1 на домен 2, но обратно вернуться уже не могут. Эта
ситуация моделирует выполнение программы с установленным битом SETUID
в UNIX. Другие переключения доменов в данном примере не разрешены.
Объект
Плоттер 2 Домен 2
Домен Файл 1 Файл 2 Файл 3 Файл 4 Файл 5 Файл 6 Принтер 1 Домен 1 Домен 3
Чтение
Чтение Enter
Запись
Чтение
Чтение
Чтение Запись Запись
Запись
Исполнение
Чтение
Запись Запись Запись
Исполнение
Пространство
пользователя
Пространство ядра
Если tana попытается получить доступ к одному из этих файлов, результат за
висит от того, к какой группе этот пользователь принадлежит в текущий момент.
Возможно, при входе система просит его указать группу, в которую он хотел бы
войти; не исключено, что для разделения групп каждой из них даже соответству
ют свои имена входа и (или) пароли. Цель такой схемы — лишить пользователя
tana возможности доступа к файлу паролей, если он вошел в систему как член
сообщества любителей голубей. Он может работать с файлом паролей только как
системный администратор.
5.5. Механизмы защиты 595
5.5.3. Мандаты
Матрица, показанная на рис. 5.23, может также храниться по рядам. Здесь с каж
дым процессом ассоциирован список разрешенных для доступа объектов вместе
с информацией о том, какие операции разрешены, другими словами, это — домен
защиты объекта. Такой список называется списком мандатов (Capability List,
C-list, рис. 5.25), а его элементы — мандатами [36, 44].
596 Глава 5. Файловые системы
Пространство
пользователя
Пространство ядра
Тайный
канал
Сервер —►
Сервер Сервер
блокирует разблокирует
файл, чтобы файл, чтобы
послать 1 послать 0
Ч------ Передаваемый
битовый поток
Время ------------ ►
Рис. 5.28. Секретный канал, основанный на блокировке файлов
5.6.1. Сообщения
Файловая система понимает 39 типов сообщений, запрашивающих различные
действия. Все они, кроме двух, соответствуют системным вызовам MINIX 3. Ис
ключения составляют два сообщения, генерируемые другими компонентами
MINIX 3. Что касается системных вызовов, то 31 из них принимаются от пользо
вательских процессов, 6 сообщений соответствуют системным вызовам, обраба
тываемым сначала менеджером памяти, который затем, чтобы завершить работу,
вызывает файловую систему. Еще 2 сообщения также обрабатываются файловой
системой. Все эти сообщения перечислены в табл. 5.5.
Таблица 5.5. Сообщения файловой системы. Имя файла всегда передается как указатель
на строку. Ответное значение, отмеченное как «состояние», равно ОК или ERROR
Сообщение Входные параметры Ответное значение
Сообщения от пользователя
access Имя файла, режим доступа Состояние
chdir Имя нового рабочего каталога Состояние
chmod Имя файла, новая спецификация доступа Состояние
chown Имя файла, новый владелец и группа Состояние
chroot Имя нового корневого каталога Состояние
close Дескриптор закрываемого файла Состояние
creat Имя создаваемого файла, режим Дескриптор файла
dup Дескриптор файла (DUP2 требует два дескриптора) Новый дескриптор файла
fcntl Дескриптор файла, код функции, аргументы Зависит от функции
fstat Имя файла, буфер Состояние
ioctl Имя файла, код функции, аргументы Состояние
link Имя файла, на который создается ссылка, имя Состояние
ссылки
Iseek Дескриптор файла, смещение, место, откуда Новое положение
считать смещение
mkdir Имя файла, спецификация доступа Состояние
mknod Имя каталога или признак специального файла, Состояние
режим доступа и адрес
mount Имя специального файла, точка монтирования, Состояние
флаг только для чтения
open Имя открываемого файла, флаг r/w Дескриптор файла
pipe Указатель на два файловых дескриптора Состояние
(модифицируются)
5.6. Обзор файловой системы MINIX 3 603
Рис. 5.29. Структура дискеты или небольшого жесткого диска с 64 индексными узлами
и размером блока 1 Кбайт (то есть два подряд идущих сектора по 512 байт
рассматриваются как один блок)
Число i-узлов
(не используется)
Число блоков битовой карты i-узлов
Число блоков битовой карты зон
Первая зона данных
Log* (количество блоков в зоне)
Присутствуют
и на диске, < Замещение страниц
и в памяти
Максимальный размер файла
Количество зон
Магическое число
Замещение страниц
Размер блока (в байтах)
Подверсия ФС
Указатель на i-узел для корня,
смонтированной файловой системы
Указатель на i-узел,
смонтированный выше
I-узлов на блок
Номер устройства
карты. Так как каждый индексный узел занимает 64 байта, блок размером 1 Кбайт
может содержать до 16 индексных узлов. При наличии 64 используемых индекс
ных узлов для их хранения потребуется 4 дисковых блока.
Позже мы подробно объясним различие между дисковыми блоками и зонами,
сейчас же достаточно сказать, что место на диске может выделяться частями (зона
ми) из 1, 2, 4, 8 или, в общем случае, 2п блоков. Битовая карта зон позволяет от
слеживать свободное пространство в зонах, а не в блоках. Для стандартных гибких
дисков, используемых MINIX 3, размер зоны совпадает с размером блока (1 Кбайт),
поэтому для таких устройств в первом приближении можно считать, что зона —
это то же самое, что и блок. Пока мы позже в этой главе не приступим к обсужде
нию деталей выделения места, вы можете считать, что эти понятия эквивалентны.
Обратите внимание, что количество блоков в зоне не хранится в суперблоке, так
как оно нигде не требуется. Все, что нужно, — это логарифм по основанию два от
этого числа, который используется как значение сдвига при преобразовании
блоков в зоны, и наоборот. Например, если зона содержит 8 блоков, log28 = 3.
То есть, чтобы найти зону, содержащую блок 128, нужно сдвинуть 128 на три бита
вправо (получится зона 16).
Битовая карта зон содержит только зоны, занимаемые данными (то есть в нее не
попадают блоки, хранящие битовые карты и индексные узлы), причем первая зо
на данных соответствует биту 1 в битовой карте. Как и в случае с картой индекс
ных узлов, нулевой бит не используется, а значит, первый блок битовой карты
описывает 8191 зон, а последующие — 8192 каждый. Если вы посмотрите на бито
вые карты только что отформатированного диска, вы увидите, что в обеих бито
вых картах (зон и индексных узлов) установлено два бита. Первый из них соот
ветствует несуществующей 0-й зоне или индексному узлу. Второй соответствует
индексному узлу и зоне корневого каталога устройства, которая создается при
создании файловой системы.
Информация, хранящаяся в суперблоке, избыточна, так как иногда она требует
ся в одной форме, а иногда в другой. Так как на размещение суперблока отводит
ся 1 Кбайт места, имеет смысл хранить информацию во всех необходимых пред
ставлениях, а не преобразовывать ее в ходе работы. Например, номер первой
зоны данных на диске можно вычислить, исходя из размера блока, размера зоны,
числа индексных узлов и числа зон, но удобнее просто хранить это значение в су
перблоке. Оставшаяся часть суперблока все равно не используется, потому вы
деление в нем одного лишнего слова ничего не стоит.
Когда загружается MINIX 3, суперблок с корневого устройства считывается в таб
лицу в памяти. Аналогичным образом, при монтировании других файловых сис
тем их суперблоки также помещаются в память. В этой таблице есть несколько
полей, которых нет на диске. Среди них флаг, индицирующий, что разрешено
только чтение, флаг, позволяющий установить нестандартный порядок следова
ния байтов, а также поля, предназначенные для ускорения доступа. Они указы
вают положение в битовых картах, ниже которого все биты установлены (то есть
заняты). Кроме того, здесь есть поле, описывающее устройство, с которого при
шел данный суперблок.
5.6. Обзор файловой системы MINIX 3 607
например, размер блока равен 1 Кбайт, а зоны — 4 Кбайт, битовая карта зон от
слеживает зоны, а не блоки. Диск объемом 20 Мбайт будет разбит на 5 К зон по
4 Кбайт, и в битовой карте зон, таким образом, будет 5 Кбит.
Большинство файловых систем работают с блоками. Обмен данными с диском
всегда производится целыми блоками, кэш также оперирует блоками. Только та
небольшая часть файловой системы, которая работает с физическим адресом
(то есть с битовой картой зон и индексными узлами), знает о зонах.
В процессе разработки файловой системы MINIX 3 необходимо было принять
некоторые решения о ее структуре. В 1985 году, когда была задумана операцион
ная система MINIX, диски имели небольшой объем, и ожидалось, что у многих
пользователей будет только дисковод. Поэтому было решено в первой версии
файловой системе ограничить дисковый адрес значением 16 бит, чтобы впоследст
вии хранить большую их часть в блоках косвенной адресации. При 16-разрядном
номере зоны и размере зоны в 1 Кбайт такая система позволяла работать с диска
ми объемом до 64 Мбайт. В те дни это был огромный объем, и казалось, что если
диски станут больше, будет несложно переключиться на зоны размером 2 Кбайт
или 4 Кбайт, не меняя размер блока. Благодаря 16-разрядному номеру зоны раз
мер индексного узла был ограничен значением 32 байта.
Когда широко распространились диски значительно большей емкости, стало оче
видно, что желательны изменения. Многие файлы имеют объем менее 1 Кбайт,
поэтому увеличение размера блока привело бы к бесполезному расходованию
объема диска из-за того, что записываются и считываются почти пустые блоки,
и к бесполезной трате драгоценной оперативной памяти на кэш. Можно было бы
увеличить размер зоны, но большие зоны означают менее эффективное использова
ние свободного места на диске, при этом все равно желательно было бы сохранить
эффективность работы с дисками маленького объема. Другой разумной альтерна
тивой было бы задание разного размера зоны для больших и маленьких дисков.
В конце концов, было принято решение увеличить разрядность дискового ука
зателя до 32 бит. Благодаря этому файловая система MINIX 2 могла бы работать
с дисками объемом до 4 Тбайт при размере зоны и блока 1 Кбайт и с дисками
объемом до 16 Тбайт при размере блока и зоны 4 Кбайт (в настоящее время
принятом по умолчанию). Однако другие факторы заставили урезать этот раз
мер (к примеру, 32-разрядные указатели не позволяют выйти за предел 4 Гбайт).
Вместе с увеличением размера индексных узлов требуется увеличивать разряд
ность дисковых указателей. Это не обязательно плохо: индексный узел MINIX 2
(а теперь и MINIX 3) совместим со стандартными индексными узлами UNIX
и имеет пространство для хранения трех значений времени, дополнительных зон
с одинарным и двойным уровнем косвенности, а также место для будущего рас
ширения зонами с тройным уровнем косвенности.
Зоны приводят еще к одной неожиданной проблеме, которую можно проиллюст
рировать следующим простым примером, опять же, с зонами размером 4 Кбайт
и блоками 1 Кбайт. Предположим, что имеется файл размером 1 Кбайт, для
которого выделена одна зона. Последние три блока зоны содержат случайный
«мусор», оставшийся от предыдущих файлов, но ничего плохого в этом нет, так
5.6. Обзор файловой системы MINIX 3 609
как в индексном узле четко указано, что размер файла равен 1 Кбайт. Фактиче
ски, этот мусор не попадет даже в дисковый кэш, так как чтение производится
блоками, а не зонами. Попытки прочитать информацию после конца файла все
гда возвращают 0 вместо данных.
Предположим, что кто-то установил файловый указатель на 32 768 и дописал
1 байт. Теперь размер файла станет равным 32 769. Если после этого установить
файловый указатель на 1 К и выполнить чтение, удастся получить данные, кото
рые были в данном блоке ранее, что является серьезной угрозой безопасности.
Решение состоит в том, чтобы в ситуации, когда производится запись после кон
ца файла, явно обнулять все еще не выделенные блоки в зоне, которая ранее бы
ла последней. Хотя такая ситуация встречается достаточно редко, система долж
на ее отслеживать, в результате ее код несколько усложняется.
16 бит
- Время доступа
Время измеряется в секундах
— Время модификации
с 1 января 1970 года
- Время изменения состояния
- Зона 0
- Зона 1
64 байта <
- Зона 2
- Зона 4
- Зона 5
- Зона 6
- Косвенная зона
Используются для файлов,
занимающих больше семи зон
— Косвенная зона второго уровня
Можно использовать для косвенных зон
- Не используется
третьего уровня вложенности
Рис. 5.31. Структура индексного узла в MINIX
Кроме того, индексный узел хранит информацию о типе файла (обычный файл,
каталог, специальный блочный файл, специальный символьный файл, канал
ввода-вывода) и биты защиты, а также биты SETUID и SETGID. В поле link
5.6. Обзор файловой системы MINIX 3 611
С целью быстрого выяснения, находится нужный блок в кэше или нет, приме
няется хеш-таблица. Все буферы, имеющие одинаковое хеш-значение k, связаны
в однонаправленный список, на который ссылается запись k хеш-таблицы. Сдела
но так по той причине, что применяемая хеш-функция просто извлекает п млад
ших битов из номера блока, и хеш-значения могут совпадать. Каждый буфер
принадлежит одной из цепочек. Конечно, сразу после загрузки MINIX 3 все
буферы свободны, поэтому все они находятся в одной цепочке, на которую ссы
лается нулевая запись хеш-таблицы. Все остальные записи таблицы в это время
содержат нулевые указатели. Но после того как система начинает работу, буфе
ры исключаются из нулевой цепочки и формируются новые цепочки.
Если файловой системе необходим блок, она вызывает функцию get_block.
Эта функция вычисляет хеш-код блока и ищет его в соответствующем списке.
612 Глава 5. Файловые системы
При вызове get_block ей передается как номер блока, так и номер устройства,
и при поиске оба эти параметра сравниваются с соответствующими полями
буферов из цепочки. Если нужный блок найден, увеличивается на единицу зна
чение счетчика в его заголовке и возвращается указатель на него. Если затребо
ванный буфер в кэше не обнаруживается, выбирается первый из LRU-списка; он
гарантированно не используется, и содержащийся в нем блок может быть удален
из оперативной памяти.
Когда удаляемый из памяти блок выбран, проверяется один из флагов в его заго
ловке, означающий, что блок был модифицирован после считывания. Если флаг
установлен, блок записывается на диск. Затем с диска считывается нужный блок,
для этого отправляется сообщение драйверу диска. До тех пор пока запрошен
ный блок не прочитан, файловая система приостанавливается, а после этого воз
вращает указатель на прочитанный блок.
По окончании работы запросившей блок процедуры, чтобы освободить блок, она
вызывает другую процедуру, put_block. Обычно блок используется сразу по
сле выделения и затем освобождается, но так как существует вероятность того,
что перед освобождением блока будут сделаны дополнительные запросы, функ
ция put_block сначала уменьшает на единицу счетчик использования и поме
щает блок обратно в LRU-список в том и только том случае, если счетчик достиг
нуля. Если он имеет ненулевое значение, блок не освобождается.
Один из аргументов функции put_block описывает, к какому классу принадле
жит освобождаемый блок (индексные узлы, каталог, данные). В зависимости от
этого значения принимаются два ключевых решения.
1. Поместить блок в начало или в конец LRU-списка.
2. Записать блок на диск немедленно (если он был модифицирован).
Почти все блоки присоединяются в конец списка в полном соответствии с идеей
сортировки по частоте использования. Исключение составляют блоки виртуаль
ного диска; поскольку они уже находятся в памяти, хранение их в кэше практи
чески бессмысленно.
Модифицированный блок не записывается до тех пор, пока не произойдет одно
из двух следующих событий.
1. Блок достиг начала LRU-списка и удаляется из памяти.
2. Выполнен системный вызов sync.
Вызов sync не перебирает элементы LRU-списка. Вместо этого он обрабатывает
массив буферов и записывает модифицированные буферы на диск даже в том
случае, если они еще используются.
Однако есть одно исключение. В старой версии MINIX суперблок модифициро
вался при монтировании системы, поэтому, чтобы снизить вероятность повреж
дения файловой системы по причине неожиданного сбоя, он сохранялся без про
медления. Сейчас суперблок модифицируется лишь в случае, если необходимо
изменить размер виртуального диска во время запуска системы потому, что он
оказался больше, чем размер устройства. Тем не менее чтение и запись супер
блока отличаются от аналогичных операций для обычного блока, поскольку размер
5.6. Обзор файловой системы MINIX 3 613
Таблица filp
данных — кэш блоков, таблица f ilp и др. — объявлены при помощи ключевого
слова EXTERN. Это же касается глобальных переменных и локальной части табли
цы процессов. Так же как и в других частях MINIX 3, память для таких перемен
ных в действительности выделяется при компиляции файла table. с. Кроме
того, в этом файле содержится важный неинициализированный массив. Массив
call_vector хранит указатели на функции, он нужен в главном цикле, чтобы
определить, какая функция какой системный вызов выполняет. Похожую табли
цу мы видели и внутри менеджера процессов.
5.7.2. Таблицы
С каждой важной таблицей, будь то таблица блоков, индексных узлов, супербло
ков и т. д., связан файл, содержащий процедуры для работы с ней. Эти процеду
ры широко используются в остальном коде и образуют основу интерфейса меж
ду таблицами и файловой системой. Поэтому наше изучение кода файловой
системы имеет смысл начать с этих файлов.
Управление блоками
С кэшем блоков работают подпрограммы из файла cache.с. Он содержит де
вять процедур, перечисленных в табл. 5.6. Первая из них, get_block (строка
22426), реализует стандартный способ получения блока данных. Когда процеду
ре файловой системы необходимо прочитать блок пользовательских данных, ка
талог, суперблок или блок любого другого типа, она вызывает функцию get_
block, указывая ей номер блока и устройство.
Когда вызывается функция get_block, она прежде всего смотрит, есть ли запро
шенный блок в кэше. Если да, возвращается указатель на него. Иначе запрошен
ный блок необходимо считать. Блоки в кэше объединены в списки, всего NR_
BUF_HASH списков. Этот параметр, NR_BUF_HASH, можно настраивать, как и па
раметр NR_BUFS, определяющий размер кэша блоков. Оба они устанавливаются
в файле include/minix/config.h. В заключение скажем несколько слов об
оптимизации размера кэша блоков и хеш-таблицы. Параметр HASH_MASK равен
626 Глава 5. Файловые системы
NR_BUF_HASH - 1. Если имеется 256 списков, маска равна 255, и все блоки, в но
мерах которых совпадают последние 8 бит, попадают в один список. Получается
256 списков для номеров 00000000, 00000001, ..., 11111111.
При поиске блока на первом шаге выясняется, в какой из цепочек хеш-таблицы
блок находится, хотя есть один особый случай, когда считывается свободное ме
сто из разреженного файла и поиск пропускается. Это — причина проверки, вы
полняемой в строке 22454. Если данный особый случай не обнаружен, следую
щие две строки устанавливают указатель Ьр на начало той цепочки, в которой
оказался бы нужный блок, если бы он был в кэше, для чего на номер блока на
кладывается маска HASH_MASK. В следующем далее цикле перебираются элемен
ты цепочки в поисках запрошенного блока. Если блок найден и в данный момент
не используется, он исключается из LRU-списка. Если он используется, то в LRU-
списке его уже нет. Затем вызывающей программе возвращается указатель на
найденный блок (строка 22463).
Если в списке нужный блок не обнаружен, то и в кэше его нет, потому из LRU-
списка выбирается дольше всех не использовавшийся блок. Выбранный блок ис
ключается из хеш-цепочки, так как ему будет назначен новый номер, и он попа
дет в другую цепочку. Если информация в блоке изменена, она записывается на
диск (строка 22495). Если это делать при помощи вызова f lushall, будут со
хранены все измененные блоки с того же устройства. Большинство блоков запи
сываются именно так. Применяемые в текущий момент блоки никогда не могут
быть отданы для другого запроса, так как они отсутствуют в LRU-списке. Одна
ко блоки почти никогда не находятся в работе, и процедура put_block обычно
освобождает блок сразу по окончании использования.
Как только получен новый буфер, во все его поля, включая поле b_dev, записы
ваются новые значения (строки 22499-22504), и можно считывать данные блока
с диска. Есть только два случая, в которых считывать диск с блока необязатель
но. Функция get_block может быть вызвана с параметром only_search. Это
может означать, что выполняется упреждающее выделение блока. Упреждающее
выделение подразумевает, что содержимое обнаруженного буфера при необхо
димости перезаписывается на диск и ему назначается новый номер, но в поле Ь_
dev заносится значение NO_DEV, как свидетельство того, что блок не содержит
данных. Пример мы увидим, когда станем обсуждать функцию rw_scattered.
Кроме того, параметр only_search может использоваться тогда, когда блок ну
жен файловой системе для полной перезаписи его содержимого. Тогда считыва
ние старых данных было бы пустым расточительством. В том и другом случа
ях параметры блока обновляются, но реальное чтение не выполняется (строки
22507-22513). Когда новый блок готов и при необходимости считан, get_block
возвращает указатель на него в вызывающую программу.
Предположим, что файловой системе временно, чтобы найти имя файла, нужен
блок каталога. Тогда, чтобы получить этот блок, она вызывает функцию get_
block. Обнаружив имя файла, она, чтобы вернуть блок в кэш, вызывает функ
цию put_block (строка 22520), освобождая буфер про запас, в расчете, что позд
нее он понадобится для другого блока.
5.7. Реализация файловой системы MINIX 3 627
Управление суперблоками
В файле super. с находятся процедуры для работы с суперблоками и битовыми
картами. Эти шесть процедур перечислены в табл. 5.8.
Как мы уже отмечали, когда требуется индексный узел или зона, вызывается
функция alloc_inode или alloc_zone. Каждая из них, в свою очередь, вызы
вает функцию alloc_bit (строка 23324), чтобы найти неустановленный бит
в битовой карте. Поиск включает в себя три вложенных цикла, работающих сле
дующим образом.
1. Внешний цикл перебирает все блоки битовой карты.
2. Промежуточный цикл перебирает все слова блока.
3. Внутренний цикл проверяет все биты в слове.
В промежуточном цикле выясняется, является ли текущее слово дополнением
нуля, то есть состоит ли оно целиком из единиц. Если да, значит, в этом слове
нет «свободных» битов, и проверяется следующее слово. Когда обнаруживается
другое значение, где по крайней мере один бит равен 0, запускается внутренний
цикл, который определяет его позицию в слове. Если оказалось, что проверены
все блоки, но нулевых битов не нашлось, значит, свободных индексных узлов
или зон на диске нет, и возвращается код NO_BIT (0). Подобный поиск может
потребовать много процессорного времени, но благодаря указателям на первый
свободный индексный узел (или зону), передаваемый из суперблока в а!1ос_
bit в качестве начальной позиции для поиска, его удается сократить.
Обнулить «занятый» бит проще, чем установить, так как не требуется выполнять
поиск. Функция f ree_bit (строка 23400) вычисляет, какой блок битовой кар
ты содержит сбрасываемый бит, и дальше вызывает функцию get_block, обну
ляет бит и завершает операцию вызовом put_block.
При помощи следующей процедуры, get_super (строка 23445), в таблице су
перблоков ищется запись для заданного устройства. Например, когда монтиру
ется файловая систем^, нужно проверить, не делается ли это повторно. Для этого
можно вызовом get_super попробовать найти устройство, на котором файло
вая система расположена. Если устройство не найдено, то и файловая система
еще не смонтирована.
В MINIX 3 сервер файловой системы способен работать с файловыми система
ми, размеры блоков которых различны, хотя в каждом разделе диска использует
ся один и тот же размер. Функция get_block_size (строка 23467) выясняет,
какой размер имеет блок в файловой системе. Она ищет заданное устройство в таб
лице суперблоков и возвращает размер блока, если устройство смонтировано.
В противном случае возвращается минимальный размер блока, MIN_BLOCK_SIZE.
Функция mounted (строка 23489) вызывается только при закрытии блочного
устройства. Обычно при закрытии такого устройства все данные, сохранен
ные в кэше, сбрасываются. Но если оказалось, что устройство смонтировано, это
нежелательно. Функции mounted передается указатель на индексный узел уст
ройства. Она проверяет, является ли устройство корневым или смонтированным,
и если устройство смонтировано, возвращает TRUE.
Наконец, перейдем к функции read_super (строка 23509). Она отчасти похожа
на функции rw_block и rw_inode, но выполняет только чтение. Суперблок не
считывается в кэш; обращение к устройству происходит для прямого считыва
ния 1024 байт, расположенных со смещением 1024 байта от начала устройства.
5.7. Реализация файловой системы MINIX 3 633
Блокировка файлов
Функции POSIX для блокировки файлов перечислены в табл. 5.9. Область фай
ла можно заблокировать на запись и чтение или только на запись при помощи
системного вызова f cntl с запросом F_SETLK или F_SETLKW. Узнать, заблоки
рована ли та или иная область файла, можно с помощью запроса F_GETLK.
Таблица 5.9. Операции блокировки записей согласно POSIX. Они выполняются по запросу
с помощью системного вызова FCNTL
Операция Действие
F_SETLK Блокирует область на запись и чтение
F_SETLKW Блокирует область на запись
F_GETLK Проверяет, свободна ли область
В файле lock. с есть только две функции. К первой из них, 1оск_ор (стро
ка 23820), обращается системный вызов fcntl при выполнении каждой из опе
раций, перечисленных в таблице. Функция делает несколько тестов, гаранти
рующих, что область в файле задана корректно. Блокировка области не должна
634 Глава 5. Файловые системы
Две следующие функции файла играют важную роль в главном цикле файловой
системы. Функция get_work (строка 24099) проверяет, имеются ли ранее за
блокированные процедуры, которые могут продолжить работу. Если такие есть,
они считаются приоритетнее новых сообщений. Только тогда, когда у файловой
системы нет отложенных задач, она отправляет ядру запрос на получение сле
дующего сообщения (строка 24124). Несколькими строками далее находится
функция reply (строка 24159), запускаемая после завершения системного вы
зова (успешного или неуспешного). Процесс может быть завершен по сигналу,
и код состояния, возвращаемый ядром, игнорируется. В этом случае все равно
ничего больше не сделаешь.
Начало Конец
к | |------ ► NULL
п ------ ► NULL
п | |------ ► NULL в
где все блоки связаны друг с другом в LRU-список и хешированы. Полезно ра
зобраться в том, как возникла ситуация, показанная на рисунке. Сразу после
инициализации процедурой buf_pool все буферы находятся в списке и все они
связаны в нулевую хеш-цепочку (рис. 5.35, а). Когда запрашивается буфер,
структура приходит в состояние, показанное на рис. 5.35, б, и остается в нем, пока
буфер используется. На этом рисунке мы можем видеть, что блок исключен из
LRU-списка и помещен в отдельную хеш-цепочку.
Обычно же блок освобождается и возвращается в LRU-список немедленно
(рис. 5.35, в). Здесь блок, хотя и не используется более, все еще содержит инфор
мацию и при необходимости может быть извлечен из хеш-цепочки. После того
как система поработает некоторое время, почти все блоки, скорее всего, окажут
ся случайно распределенными между различными цепочками. LRU-список при
этом будет выглядеть так, как показано на рис. 5.32.
Следующую функцию, build_dmap, мы опишем позже, когда пойдет речь о функ
циях, работающих с файлами устройств. После этого вызывается функция load_
ram, которая, в свою очередь, использует функцию igetenv (строка 2641). По
следняя получает от ядра численный идентификатор, передавая ему в качестве
аргумента параметр загрузки. Если вы пользовались командой sysenv для про
смотра параметров загрузки работающей системы MINIX 3, вы видели, что ин
формация отображалась в виде строк, подобных следующей:
rootdev=912
осуществляет запись в суперблок, однако, как и при чтении последнего, кэш бло
ков не используется и данные пишутся непосредственно на устройство с помо
щью функции dev_io.
Сейчас два момента заслуживают комментария. Первый — код в строках 24291-
24307, обрабатывающий вариант загрузки с компакт-диска. В нем используется
функция cdprobe, не рассматриваемая в этой книге. Заинтересованным читате
лям мы предлагаем обратиться к коду файла fs/cdprobe. с, который можно
найти на компакт-диске и веб-сайте MINIX 3. Второй момент: независимо от ис
пользуемого MINIX 3 размера обычных блоков, размер загрузочного блока всегда
равен 1 Кбайт, а суперблок загружается из второго килобайта дискового устрой
ства. Любые другие варианты оказались бы сложными, поскольку размер блока
можно узнать, лишь загрузив суперблок.
Функция load_ram выделяет пространство под пустой виртуальный диск, если
указан ненулевой размер ramsize, при этом виртуальный диск не используется
в качестве корневой файловой системы. Виртуальный диск будет можно задей
ствовать как файловую систему лишь после инициализации командой mkfs,
поскольку структуры файловой системы не скопированы в него. В качестве аль
тернативы такой виртуальный диск можно сделать вторичным кэшем, если соот
ветствующая поддержка интегрирована в файловую систему.
Последняя функция файла main, с, функция load_super (строка 24426), ини
циализирует таблицу суперблоков и считывает в нее суперблок корневого уст
ройства.
индексный узел и делает для него запись каталога. Фактически, большую часть
работы выполняет функция new_node (строка 24797). Если индексный узел уже
существует, возвращается код ошибки. Код ошибки тот же самый, который был
приемлемым при открытии файла. Однако в данном случае код возвращается
в вызывающую программу, которая, предположительно, подобающим образом его
интерпретирует. Подробный анализ отдельных случаев, проводимый в common_
open, здесь не нужен.
Функция do_mkdir (строка 24805) выполняет системный вызов mkdir. Как
и в случае с предыдущими системными вызовами, важную роль здесь играет
функция new_node. Каталоги в отличие от файлов никогда не бывают пустыми,
так как любой каталог содержит, по крайней мере, две записи, точку (.) и две
точки (. .), первая из которых ссылается на сам каталог, а вторая — на вышестоя
щий. Количество ссылок на один файл ограничено константой LINK_MAX (в файле
include/limits .h для стандартной конфигурации MINIX 3, предназначенной
для платформы Intel, она определена как SHRT_MAX и ее значение равно 32767).
Поэтому, раз дочерний каталог содержит ссылку на родительский, функция do_
mkdir сначала проверяет, можно ли добавить новую ссылку на родительский ка
талог (строки 24819-24820). Когда эта проверка пройдена, вызывается функция
new_node. Если и этот вызов удался, для каталогов создаются записи . и . .
(строки 24841-24842). Описанный алгоритм прямолинеен, но учитывает возмож
ность сбоев (например, переполнение диска). Чтобы ничего не испортить, обес
печивается откат к исходному состоянию, если процесс не может быть завершен.
Закрыть файл всегда проще, чем открыть. Поэтому функции do_close (стро
ка 24865) для обычных файлов, фактически, требуется только уменьшить значе
ние счетчика в f ilp и, если оно достигло нуля, возвратить индексный узел при
помощи функции put_inode. (Каналам же и специальным файлам требуется
особое внимание.) На последнем шаге аннулируются все блокировки, связанные
с этим файлом, и пробуждаются все процессы, приостановленные по факту бло
кировки.
Заметьте, что возврат индексного узла означает только, что уменьшается счетчик
в таблице индексных узлов, следовательно, в конце концов, он может быть уда
лен из таблицы. Эта операция не имеет ничего общего с освобождением индекс
ного узла (то есть со сбросом бита в битовой карте индексных узлов). Объект
индексного узла освобождается только тогда, когда файл больше не находится
ни в одном из каталогов.
Последняя процедура в файле open, с — процедура do_lseek (строка 24939).
Она вызывается, когда осуществляется переход на заданную позицию в файле. При
этом блокируется упреждающее чтение (строка 24968), поскольку явное переме
щение на заданную позицию в файле несовместимо с последовательным доступом.
Чтение файла
Открыв файл, его можно читать или записывать в него данные. Функций для
этого больше, чем достаточно, — все связанные с чтением функции можно найти
в файле read. с. Мы обсудим сначала их, а затем перейдем к следующему файлу,
642 Глава 5. Файловые системы
Номера байтов
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Рис. 5.36. Три примера того, как для 10-байтового файла определяется размер первого
фрагмента данных. Размер блока равен 8 байт, запрашивается 6 байт.
Фрагмент отмечен штриховкой
Для блоков, расположенных дальше, может понадобиться считать один или боль
ше блоков косвенной адресации.
Функция rd_indir (строка 25400) вызывается, когда необходимо считать кос
венный блок. Комментарии к ней несколько устарели; во-первых, код поддержки
процессора 68000 удален, во-вторых, файловая система MINIX 1 больше не ис
пользуется и соответствующий ей код также можно исключить. Тем не менее
следует отметить, что в случае организации поддержки других файловых систем
или платформ, проблемы иных форматов сохранения данных на диске, типов дан
ных и порядка следования байтов можно решить в этом файле. Если без труд
ных для понимания преобразований данных не обойтись, то совершив их здесь,
вы обеспечите единую форму представления данных во всей файловой системе.
Функция read_ahead (строка 25432) преобразует логическое положение в фи
зический адрес блока, вызывает функцию get_block, в результате чего блок
оказывается в кэше, и немедленно возвращает его. В конце концов, сделать с ним
она все равно ничего не может. Задача read_ahead лишь в том, чтобы повысить
вероятность найти данные в кэше, если они скоро понадобятся.
Обратите внимание, что функция read_ahead вызывается только из главного
цикла в main. Ее вызов не является частью вызова read. Важно понять, что эта
функция вызывается после того, как пользователю отправлен ответ, чтобы он
мог продолжать свою работу, пока файловая система дожидается завершения
упреждающего чтения.
Сама функция read_ahead написана так, чтобы запрашивать всего один блок.
Реально трудится вызываемая ею подпрограмма rahead. В rahead (строка 25451)
заложена та жизненная концепция, что если немного больше — хорошо, а намно
го больше — еще лучше. Так как дискам и прочим накопителям часто требуется
много времени, чтобы найти первый запрошенный блок, но зато они могут быст
ро считать несколько смежных блоков, делается ставка на то, что получится счи
тать много последовательных блоков ценой небольших дополнительных затрат.
Запрос на упреждающую выборку передается функции get_block, подготавли
вающей кэш блоков к получению нескольких блоков за раз. Затем вызывается
функция rwjscattered, которой передается список блоков. Работу этой функции
мы уже обсуждали. Вспомните, что rw_scattered передает запрос драйверу
устройства, который в ответ имеет право обслужить столько запросов, сколько
он способен выполнить эффективно. Все это звучит довольно витиевато, но зато
позволяет на нужное «много» повысить быстродействие приложений, считываю
щих с диска ожидаемое «больше» данных.
На рис. 5.37 показаны взаимосвязи между основными подпрограммами, участ
вующими в чтении файла. В том числе указано, кто кого вызывает.
Запись
Код, обеспечивающий запись, находится в файле write. с. Запись в файл в боль
шинстве своем сходна с чтением, и функция do_write (строка 25625) просто
вызывает read_write с флагом WRITING. Основное отличие записи в том, что
здесь может потребоваться выделение дополнительных блоков. Функция write_
5.7. Реализация файловой системы MINIX 3 645
map (строка 25635) аналогична read_map с той разницей, что вместо поиска
физического адреса блока по индексному узлу и косвенным блокам она добавля
ет номер новой зоны, а не блока.
Точки входа
do read
Доступ к косвенному
блоку Поиск в кэше
(если нужно) С rw_block
dev ю
Опять же, если бы в случае неудачи можно было просто сообщить о фатальной
ошибке ядра, код был бы намного компактнее. Однако с точки зрения пользовате
ля, гораздо лучше, когда при переполнении диска write возвращает код ошибки
вместо того, чтобы провоцировать крах, к тому же с разрушением файловой системы.
Функция wr_indir (строка 25726) записывает новый номер зоны в косвенный
блок, а чтобы преобразовать данные в нужный формат (порядок следования бай
тов), она вызывает подпрограмму преобразования conv4. Здесь мы снова имеем
дело с устаревшим кодом, работающим с файловой системой версии 1; на самом
деле, используется лишь код, работающий с версией 2. Пусть имя этой функции
не вводит вас в заблуждение. Помните, что действительную запись данных на
диск выполняют функции, обслуживающие кэш блоков.
Далее в файле write.с следует функция clear_zone (строка 25747). Она за
нимается очисткой блоков, оказавшихся в середине файла. Такое может слу
читься, если записать некоторый объем данных после конца файла. К счастью,
это случается не очень часто.
Функция new_block (строка 25787) вызывается из rw_chunk, когда требуется
новый блок. На рис. 5.38 показаны шесть последовательных этапов увеличения
файла. В этом примере размер блока равен 1 Кбайт, а зоны — 2 Кбайт.
Рис. 5.38. Последовательное выделение блоков размером 1 Кбайт при размере зоны 2 Кбайт
Здесь при первом своем вызове функция new_block выделяет зону 12 (блоки
24 и 25). Затем она задействует блок 25, который уже выделен, но еще не исполь
зован. При третьем вызове выделяется зона 20 (блоки 40 и 41) и т. д.
Функция zero_block (строка 25839) очищает блок, стирая его предыдущее со
держимое. Честно говоря, это описание длиннее, чем сам код.
Каналы ввода-вывода
Каналы во многих отношениях подобны файлам. В этом разделе мы сконцен
трируем внимание на отличиях. Код, который мы будем обсуждать, находится
в файле pipe . с.
5.7. Реализация файловой системы MINIX 3 647
Хотя может показаться, что нет смысла бесконечно повторять проверки, практи
ка показывает, что в реальных операционных системах значительная часть кода
выполняет рутинную работу, которая не слишком интересна, но имеет ключевое
значение для системы. Если пользователь иногда случайно будет пытаться мон
тировать поврежденную дискету, это приведет к тотальной порче файловой сис
темы, и он решит, что система ненадежна, в чем виноватым, естественно, окажет
ся не пользователь, а разработчик.
Томас Эдисон сделал одно замечание, которое применимо и к нашему случаю.
Он сказал, что гений — это 1 % вдохновения и 99 % труда. Различие между хоро
шей и посредственной системой состоит не в превосходном алгоритме планиро
вания, а в том внимании, которое уделено деталям.
Демонтировать файловую систему проще, чем монтировать. Эту задачу в два
этапа решает функция do_umount (строка 26828). Она убеждается, что вызов
был сделан суперпользователем, преобразует имя в номер устройства, а затем
обращается к подпрограмме unmount (строка 26846), завершающей операцию.
Единственная проблема в том, что все файлы на демонтируемой файловой сис
теме должны быть закрыты, и ни у одного процесса не должно быть текущего
каталога на ней. Проверка осуществляется тривиально: сканируется вся табли
ца индексных узлов на предмет поиска в памяти хотя бы одного индексного
узла, принадлежащего демонтируемой файловой системе. Если да, umount рапор
тует об ошибке.
Последняя подпрограмма в файле mount .с носит имя name_to_dev (строка
26893). Она определяет главный и вспомогательный номера устройства по пере
даваемому ей имени специального файла. Эти номера хранятся в самом индекс
ном узле, там, где у обычных файлов хранится информация о первой зоне. Это
место пустует, поскольку у специальных файлов нет зон.
Вот некоторые из возможных ошибок, которые могут произойти при этом вызове:
+ файл f ile_name не существует или недоступен;
+ у файла f ile_name уже есть максимальное количество ссылок;
♦ файл file_name является каталогом (создавать такие ссылки имеет право
только суперпользователь);
+ файл link_name уже существует;
+ файлы link_name и f ile_name расположены на разных устройствах.
Если все в порядке, в каталоге создается новая запись с именем link_name и но
мером индексного узла file_name. В коде имя namel соответствует file
name, a name2 — link_пате. Вносит новую запись в каталог функция search-
dir, вызываемая из do_link в строке 27086.
5.7. Реализация файловой системы MINIX 3 651
Защита
Механизм защиты в MINIX 3 основан на битах rwx. Это три набора битов, каж-
дый из которых определяет доступность файла для его владельца, группы и для
остальных. Значениями этих битов можно управлять при помощи системного
вызова chmod, инициируемого функцией do_chmod из файла protect .с (стро
ка 27824). Она сначала делает ряд проверок, а затем меняет режим доступа к фай
лу (строка 27850).
Вызов chown подобен вызову chmod в том, что он тоже меняет значения внут
ренних полей индексного узла некоторого файла. Поэтому их реализация тоже
достаточно схожа, хотя do_chown (строка 27862) позволяет менять владельца
файла только суперпользователю. Обычные пользователи вправе применять этот
вызов, чтобы менять принадлежность их собственных файлов к группе.
Вызов umask позволяет задать маску (хранящуюся в таблице процессов), которая
маскирует биты разрешений при последующих вызовах creat. Весь код уместил
ся бы в одну строку (27907), если бы не потребность в восстановлении старого
значения маски. Это дополнительное требование утраивает объем кода (стро
ки 27906-27908).
При помощи системного вызова access процесс может выяснить, разрешен ли
ему определенный способ доступа к файлу (например, на чтение). Этот вызов
реализуется функцией do_access (строка 27914), которая считывает индекс
ный узел файла, а затем вызывает вспомогательную функцию forbidden (стро
ка 27938). Та, в свою очередь, проверяет UID и GID процесса, а также инфор
мацию в индексном узле и в зависимости от этих данных принимает решение
о том, разрешен доступ или запрещен.
Небольшая процедура read_only проверяет файловую систему, в которой распо
ложен переданный ей индексный узел, и определяет, смонтирована ли она с дос
тупом только на чтение. Эта функция необходима для предотвращения попыток
записи в такую файловую систему.
654 Глава 5. Файловые системы
Первое нулевое значение указывает на то, что запись относится к драйверу, ко
торый не обязан быть в загрузочном образе. По умолчанию при попытке откры
тия устройства первый указатель вызывает функцию no_dev, возвращающую
вызвавшему процессу ошибку ENODEV (нет такого драйвера). Следующие два
нулевых значения также используются по умолчанию: поскольку устройство
невозможно открыть, нет необходимости вызывать функцию для фактического
ввода-вывода. Нуль в элементе таблицы процессов интерпретируется как отсут
ствие процесса. Значение DMAP_MUTABLE указывает на то, что изменения этой
записи разрешены (обратите внимание, что отсутствие такого флага в записи
драйвера памяти означает невозможность ее модификации после инициализа
ции). Система MINIX 3 может быть сконфигурирована как на наличие, так и на
отсутствие драйвера дисковода для дискет в загрузочном образе. Если драйвер
присутствует и определен параметром загрузки label = FLOPPY как дисковое
устройство по умолчанию, при запуске файловой системы его запись изменяет
ся. Если драйвера нет в загрузочном образе либо он не является драйвером по
умолчанию, его запись не изменяется с запуском файловой системы. Тем не ме
нее возможность активации драйвера дискет позднее сохраняется. Как правило,
ее выполняет сценарий /etc/rc при запуске init.
Функция do_devctl (строка 28157) исполняется первой при обслуживании вы
зова devctl. Текущая ее версия очень проста: она распознает два запроса, DEV_
МАР и DEV_UNMAP, причем в последнем случае возвращается ошибка ENOSYS
(функция не реализована). Очевидно, эта мера временная; в случае запроса DEV_
МАР вызывается вторая функция, map_driver.
656 Глава 5. Файловые системы
Время
Каждому файлу сопоставлены три 32-разрядных числа, связанные со временем.
Первые два хранят время последнего доступа к файлу и момент его последнего
изменения. В третьем фиксируется время последнего изменения состояния са
мого индексного узла. Это время меняется практически при каждом обращении
к файлу, за исключением вызовов read и ехес. Все значения хранятся в индекс
ном узле. При помощи системного вызова utime владелец файла или суперполь
зователь может изменить время доступа и изменения. Это делается процедурой
do_utime (строка 28818) из файла time . с, которая получает индексный узел
и записывает в него значения времени. Затем сбрасываются флаги, индицирующие,
660 Глава 5. Файловые системы
что требуется обновить время (строка 28848), с целью избежать затратного и не
нужного здесь вызова clock_time.
Как мы знаем из предыдущей главы, реальное время рассчитывается как сумма
времени, отсчитанного от последнего запуска системы и поддерживаемого таймер
ным заданием, и реального времени запуска. Возврат значения реального времени
осуществляет вызов stime. Большую часть его работы делает менеджер процессов,
однако глобальная переменная boot time, хранящая время запуска системы, под
держивается файловой системой. Каждый раз при поступлении вызова stime
менеджер процессов посылает файловой системе сообщение, благодаря кото
рому ее функция do_stime (строка 28859) обновляет переменную boot time.
Таблица 5.10. Параметры запроса системного вызова FCNTL согласно стандарту POSIX
Действие Значение
F.DUPFD Дублирование дескриптора
F.GETFD Считывание флага закрытия файла при системном вызове ехес
F_SETFD Установка флага закрытия файла при системном вызове ехес
F.GETFL Считывание флагов состояния файла
F_SETFL Установка флагов состояния файла
F_GETLK Считывание состояния блокирования файла
F.SETLK Блокирование чтения и записи в файл
F_SETLKW Блокирование записи в файл
Резюме
Со стороны файловая система представляется как коллекция файлов, каталогов
и инструментов для действий над ними. Можно считывать или записывать фай
лы, создавать и уничтожать каталоги и перемещать файлы из одного каталога
в другой. В большинстве современных файловых систем поддерживается иерар
хическая структура каталогов, когда в один каталог может быть вложен второй
и т. д., до бесконечности.
Если же смотреть изнутри, открывается совершенно другая картина. Разработ
чики файловой системы должны заботиться о том, как выделяется место, и от
слеживать, какой блок какому файлу соответствует. Мы увидели, что в разных
файловых системах структуры каталогов различаются. Надежность и произво
дительность файловой системы тоже имеют существенное значение.
Исключительно важны как для пользователей, так и для разработчиков системы
вопросы безопасности и защиты. Мы обсудили известные уязвимости старых
664 Глава 5. Файловые системы
систем и общие проблемы, волне вероятные для многих других. Размышляя о за
щите, мы рассмотрели аутентификацию, с паролем и без, списки управления
доступом, мандатные системы, а также матричную модель защиты.
Наконец, мы подробно изучили файловую систему MINIX 3. Ее код весьма объ
емен, но не очень сложен. Файловая система принимает запросы от пользователь
ских процессов, находит в таблице системных вызовов адреса процедур и вызы
вает эти процедуры, чтобы обслуживать запросы. Благодаря модульной структуре
и тому, что файловая система вынесена из ядра, ее можно легко выделить из
MINIX 3, превратив в самостоятельный сетевой файловый сервер, внеся лишь
незначительные поправки.
При обращении к файлу MINIX 3 буферизует блоки в кэше и пытается делать
упреждающее чтение при последовательном режиме работы с файлом. Если кэш
достаточно велик, то при многократных обращениях к одному и тому же набору
программ (например, в процессе компиляции) нужный файл с высокой вероят
ностью окажется в памяти.
Вопросы и задания
1. В файловой системе NTFS для именования файлов используется кодировка
Unicode, поддерживающая 16-разрядные символы. В чем преимущество име
нования файлов в Unicode по сравнению с ASCII?
2. Некоторые файлы начинаются с «магического» числа. Для чего оно исполь
зуется?
3. В табл. 5.2 перечислены некоторые атрибуты файлов, однако в ней отсутству
ет атрибут четности. Является ли четность полезным атрибутом для файла?
Если да, то как ее можно использовать?
4. Создайте пять различных путей к файлу /etc/passwd. Подсказка: исполь
зуйте в каталоге записи точка (.) и две точки (. .).
5. В системах, поддерживающих последовательный доступ, всегда имеется опе
рация «перемотки» файлов. Нужна ли такая операция в системах, поддержи
вающих файлы произвольного доступа?
6. В некоторых операционных системах предоставляется системный вызов rename,
позволяющий сменить имя файла. Есть ли разница между использованием
этого системного вызова и копированием файла с новым именем с последую
щим удалением старого файла?
7. Рассмотрите дерево каталогов на рис. 5.5. Если /usr/ j im является рабочим
каталогом, как будет выглядеть абсолютный путь для файла с относительным
путем ../ast/x?
8. Предположим, у файловой системы нет единого корневого каталога, а вместо
этого у каждого пользователя имеется собственный корневой каталог. Делает
ли это файловую систему более гибкой? Обоснуйте ответ.
Вопросы и задания 665
36. Напишите две программы на языке С или в виде сценария интерпретатора, пе
редающие и принимающие сообщение по тайному каналу в системе MINIX 3.
Подсказки: во-первых, бит разрешения видим, даже если другие способы
доступа к файлу запрещены, во-вторых, команда или системный вызов sleep
гарантирует задержку фиксированной длительности, заданную аргументом.
Измерьте скорость обмена данными в простаивающей системе, затем создай
те искусственную высокую нагрузку, запустив множество фоновых процес
сов, и снова измерьте скорость обмена данными.
37. Реализуйте в MINIX 3 непосредственные файлы (маленькие файлы, храни
мые в индексном узле и экономящие время доступа к диску).
Глава 6
Библиография
В предыдущих пяти главах нами были затронуты разнообразные темы. Эта глава
призвана помочь читателям, желающим заняться углубленным изучением опе
рационных систем. В пункте 6.1 приведен список рекомендуемой литературы,
а в пункте 6.2 — еще один список, в котором в алфавитном порядке перечислены
все публикации, цитаты из которых использованы в этой книге.
Полезными источниками публикаций об операционных системах являются сбор
ники докладов Симпозиума по принципам операционных систем (Symposium
on Operating Systems Principles), проводимого раз в два года, Международной
конференции по распределенным компьютерным системам (International Con
ference on Distributed Computing Systems), проводимой IEEE ежегодно, а также
Симпозиума USENIX по проектированию и реализации операционных сис
тем. Кроме того, в журналах ACM Transactions on Computer Systems и Operating
Systems Review периодически публикуются статьи, имеющие отношение к дан
ной тематике.
6.1.2. Процессы
♦ Andrews and Schneider, «Concepts and Notations for Concurrent Programming».
Учебное пособие по процессам и взаимодействию между процессами, вклю
чая активное ожидание, семафоры, мониторы, обмен сообщениями и прочие
методы. В данной статье также показано, как эти концепции поддерживаются
встроенными средствами различных языков программирования.
672 Глава 6. Библиография
6.1.3. Ввод-вывод
♦ Chen et al., «RAID: High Performance Reliable Secondary Storage».
Параллельное использование нескольких жестких дисков для ускорения вво
да-вывода характерно для высокопроизводительных систем. Авторы рас
сматривают эту концепцию и исследуют различные варианты ее организации
с точки зрения производительности, стоимости и надежности.
♦ Coffman et al., «System Deadlocks».
Краткий обзор взаимных блокировок, их причин, способов обнаружения и пре
дотвращения.
♦ Corbet et al., Linux Device Drivers, 3rd Ed.
Если вы действительно хотите понять, как работает ввод-вывод, попробуйте
написать драйвер устройства. Эта книга поможет вам сделать это для опера
ционной системы Linux.
♦ Geist and Daniel, «А Continuum of Disk Scheduling Algorithms».
Описан обобщенный алгоритм планирования головки диска, а также пред
ставлены многочисленные экспериментальные данные и результаты модели
рования.
♦ Holt, «Some Deadlock Properties of Computer Systems».
Рассматриваются взаимные блокировки. Автор представляет модель на осно
ве направленных графов, с помощью которой можно анализировать некото
рые ситуации взаимных блокировок.
6.1. Рекомендуемая литература 673
5. Bala, К., Kaashoek, M.F., and Weihl, W.: «Software Prefetching and Caching for
Translation Lookaside Buffers,» Proc. First Symp. on Oper, Syst. Design and Imp
lementation, USENIX, pp. 243—254, 1994.
6. Basili, V.R., and Perricone, B.T.: «Software errors and Complexity: An Empirical
Investigation,» Commun. of the ACM, vol. 27, pp. 43—52, Jan. 1984.
7. Bays, С.: «А Comparison of Next-Fit, First-Fit, and Best-Fit,» Commun. of the
ACM, vol. 20, pp. 191-192, March 1977.
8. Ben-Ari, M: Principles of Concurrent and Distributed Programming, Upper Saddle
River, NJ: Prentice Hall, 1990.
9. Bic, L.F., and SHAW, A.C.: Operating System Principles, Upper Saddle River, NJ:
Prentice Hall, 2003.
10. Boehm, H.-J.: «Threads Cannot be Implemented as a Library,» Proc. 2004 ACM
SIGPLAN Conf, on Prog. Lang. Design and Impl., ACM, pp. 261—268, 2005.
11. Bovet, D.P., and CESATI, M.: Understanding the Linux Kernel, 2nd Ed., Sebastopol,
CA, O’Reilly, 2002.
12. Brinch Hansen, P.: Operating System Principles Upper Saddle River, NJ: Prentice
Hall, 1973.
13. Brinch Hansen, P.: Classic Operating Systems, New York: Springer-Verlag, 2001.
14. Brooks, F. P., Jr.: The Mythical Man-Month: Essays on Software Engineering,
Anniversary Ed., Boston: Addison-Wesley, 1995.
15. Cerf, V.G.: «Spam, Spim, and Spit,» Commun. of the ACM, vol. 48, pp. 39—43,
April 2005.
16. Chen, H, Wagner, D., and Dean, D.: «Setuid Demystified,» Proc. 11th USENIX
Security Symposium, pp. 171-190, 2002.
17. Chen, P.M., Lee, E.K., Gibson, G.A., Katz, R.H., and Patterson, D.A.: «RAID: High
Performance Reliable Secondary Storage,» Computing Surveys, vol. 26, pp. 145—
185, June 1994.
18. Cheriton, D.R.: «An Experiment Using Registers for Fast Message-Based Inter
process Communication,» Operating Systems Review, vol. 18, pp. 12—20, Oct. 1984.
19. Chervenak, A., Vellanski, V., and Kurmas, Z.: «Protecting File Systems: A Survey
of Backup Techniques,» Proc. 15th Symp. on Mass Storage Systems, IEEE, 1998.
20. Chou, A., Yang, J.-F., Chelf, B., and Hallem, S.: «An Empirical Study of Operating
System Errors,» Proc. 18th Symp. on Oper. Syst. Prim, ACM, pp. 73—88, 2001.
21. Coffman, E.G., Elphick, MJ., and Shoshani, A.: «System Deadlocks,» Computing
Surveys, vol. 3, pp. 67—78, June 1971.
22. Corbato’, FJ.: «On Building Systems That Will Fail,» Commun. of the ACM, vol.
34, pp. 72-81, Sept. 1991.
23. Corbato’, FJ., Merwin-Daggett, M., and Daley, R.C: «An Experimental Time-Sharing
System,» Proc. AFIPS Fall Joint Computer Conf., AFIPS, pp. 335—344, 1962.
24. Corbato’, FJ., Saltzer, J.H., and Clingen, C.T.: «MULTICS — The First Seven
Years,» Proc. AFIPS Spring Joint Computer Conf., AFIPS, pp. 571—583, 1972.
6.2. Алфавитный список литературы 677
25. Corbato’, FJ., and Vyssotsky, V.A.: «Introduction and Overview of the MULTICS
System,» Proc. AFIPS Fall Joint Computer Conf., AFIPS, pp. 185—196, 1965.
26. Corbet, J., Rubini, A., and Kroah-Hartman, G.: Linux Device Drivers, 3rd Ed.
Sebastopol, CA: O’Reilly, 2005.
27. Courtois, PJ., Heymans, F., and Parnas, D.L.: «Concurrent Control with Readers
and Writers,» Commun. of the ACM, vol. 10, pp. 667—668, Oct. 1971.
28. Daley, R.C., and Dennis, J.B.: «Virtual Memory, Processes, and Sharing in
MULTICS,» Commun. of the ACM, vol. 11, pp. 306-312, May 1968.
29. Deitel, H.M., Deitel, P. J., and Choffnes, D. R.: Operating Systems, 3rd Ed., Upper
Saddle River, NJ: Prentice-Hall, 2004.
30. Denning, D.: «The United states vs. Craig Neidorf,» Commun. of the ACM, vol. 34,
pp. 22-43, March 1991.
31. Denning, PJ.: «The Working Set Model for Program Behavior,» Commun. of the
ACM, vol. 11, pp. 323-333, 1968a.
32. Denning, P.J.: «Thrashing: Its Causes and Prevention,» Proc. AFIPS National
Computer Conf., AFIPS, pp. 915-922, 1968b.
33. Denning, PJ.: «Virtual Memory,» Computing Surveys, vol. 2, pp. 153—189, Sept.
1970.
34. Denning, PJ.: «Working Sets Past and Present,» IEEE Trans, on Software Engi
neering, vol. SE-6, pp. 64—84, Jan. 1980.
35. Denning, PJ.: «The Locality Principle,» Commun. of the ACM, vol. 48, pp. 19—24,
July 2005.
36. Dennis, J.B., and Van Horn, E.C.: «Programming Semantics for Multiprogrammed
Computations,» Commun. of the ACM, vol. 9, pp. 143—155, March 1966.
37. DiBona, C., Ockman, S., and Stone, M. eds.: Open Sources: Voices from the Open
Source Revolution, Sebastopol, CA: O’Reilly, 1999.
38. Dijkstra, E.W.: «Co-operating Sequential Processes,» in Programming Languages,
Genuys, F. (Ed.), London: Academic Press, 1965.
39. Dijkstra, E.W.: «The Structure of THE Multiprogramming System,» Commun. of
the ACM, vol. 11, pp. 341-346, May 1968.
40. Dijkstra, E.W.: «My Recollections of Operating System Design,» Operating Systems
Review, vol. 39, pp. 4—40, April 2005.
41. Dodge, C., Irvine, C., and Nguyen, T.: «А Study of Initialization in Linux and
OpenBSD,» Operating Systems Review, vol. 39, pp. 79—93 April 2005.
42. Engler, D., Chen, D.Y., and Chou, A.: «Bugs as Inconsistent Behavior: A General
Approach to Inferring Errors in Systems Code,» Proc. 18th Symp. on Oper. Syst.
Prim, ACM, pp. 57-72, 2001.
43. Engler, D.R., Kaashoek, M.F., and O’Toole, J. Jr.: «Exokernel: An Operating System
Architecture for Application-Level Resource Management,» Proc. 15th Symp. on
Oper. Syst. Prim, ACM, pp. 251—266, 1995.
678 Глава 6. Библиография
44. Fabry, R.S.: «Capability-Based Addressing,» Commun. of the ACM, vol. 17,
pp. 403-412, July 1974.
45. Feeley, MJ., Morgan, W.E., Pighin, F.H., Karlin, A.R., Levy, H.M., and Thekkath,
C.A.: «Implementing Global Memory Management in a Workstation CLuster,»
Proc. 15th Symp. on Oper. Syst. Prim, ACM, pp. 201—212, 1995.
46. Feustal, E.A.: «The Rice Research Computer — A Tagged Architecture,» Proc.
AFIPS Conf. 1972.
47. Fotheringham, J.: «Dynamic Storage Allocation in the Atlas Including an Automatic
Use of a Backing Store,» Commun. of the ACM, vol. 4, pp. 435—436, Oct. 1961.
48. Garfinkel, S.L., and Shelat, A.: «Remembrance of Data Passed: A Study of Disk
Sanitization Practices,» IEEE Security & Privacy, vol. 1, pp. 17—27, Jan.-Feb. 2003.
49. Geist, R., and Daniel, S.: «А Continuum of Disk Scheduling Algorithms,» ACM
Trans, on Computer Systems, vol. 5, pp. 77—92, Feb. 1987.
50. Ghemawat, S., Gobioff, H., and Leung., S.-Т.: «The Google File System,» Proc.
19th Symp. on Oper. Syst. Prim, ACM, pp. 29—43, 2003.
51. Graham, R.: «Use of High-Level Languages for System Programming,» Project
MAC Report TM-13, M.I.T., Sept. 1970.
52. Hafner, K., and Markoff, J.: Cyberpunk: Outlaws and Hackers on the Computer
Frontier, New York: Simon and Schuster, 1991.
53. Halpern, M.: «VIM: Taming Software with Hardware,» IEEE Computer, vol. 36,
pp. 21-25, Oct. 2003.
54. Harbron, T.R.: File Systems: Structures and Algorithms, Upper Saddle River, NJ:
Pentice Hall, 1988.
55. Harris, S., Harper, A., Eagle, C., Ness, J., and Lester, M.: Gray Hat Hacking: The
Ethical Hacker’s Handbook, New York: McGraw-Hill Osborne Media, 2004.
56. Hauser, C., Jacobi, C., Theimer, M., Welch, B., and Weiser, M.: «Using Threads in
Interactive Systems: A Case Study,» Proc. 14th Symp. on Oper. Syst. Prim, ACM,
pp. 94-105, 1993.
57. Hebbard, B. et al.: «А Penetration Analysis of the Michigan Terminal System»
Operating Systems Review, vol. 14, pp. 7—20, Jan. 1980.
58. Herborth, C.: UNIX Advanced: Visual Quickpro Guide, Berkeley, CA: Peachpit
Press, 2005.
59. Herder, J.N.: «Towards a True Microkernel Operating System,» M.S. Thesis, Vrije
Universiteit, Amsterdam, Feb. 2005.
60. Hoare, C.A.R.: «Monitors, An Operating System Structuring Concept,» Commun.
of the ACM, vol. 17, pp. 549—557, Oct. 1974; Erratum in Commun. of the ACM,
vol. 18, p. 95, Feb. 1975.
61. Holt, R.C: «Some Deadlock Properties of Computer Systems,» Computing Surveys,
vol. 4, pp. 179-196, Sept. 1972.
62. Huck, J., and Hays, J.: «Architectural Support for Translation Table Management
in Large Address Space Machines,» Proc. 20th Annual Int’l Symp. on Computer
Arch., ACM, pp. 39-50, 1993.
6.2. Алфавитный список литературы 679
63. Hutchinson, N.C., Manley, S., Federwisch, M., Harris, G., Hitz, D, Kleiman, S, and
O’Malley, S.: «Logical vs. Physical File System Backup,» Proc. Third USENIX
Symp. on Oper. Syst. Design and Implementation, USENIX, pp. 239—249, 1999.
64. IEEE: Information technology — Portable Operating System Interface (POSIX),
Part 1: System Application Program Interface (API) [C Language], New York:
IEEE, 1990.
65. Jacob, B., and Mudge, T.: «Virtual Memory: Issues of Implementation,» IEEE
Computer, vol. 31, pp. 33—43, June 1998.
66. Johansson, J., and Riley, S: Protect Your Windows Network: From Perimeter to
Data, Boston: Addison-Wesley, 2005.
67. Kernighan, B.W., and Ritchie, D.M.: The C Programming Language, 2nd Ed.,
Upper Saddle River, NJ: Prentice Hall, 1988.
68. Klein, D.V.: «Foiling the Cracker: A Survey of, and Improvements to, Password
Security,» Proc. UNIX Security Workshop II, USENIX, Aug. 1990.
69. Kleinrock, L.: Queueing Systems, Vol. 1, New York: John Wiley, 1975.
70. Knuth, D.E.: The Art of Computer Programming, Volume 1: Fundamental Algo
rithms, 3rd Ed., Boston: Addison-Wesley, 1997.
71. Lampson, B.W.: «А Scheduling Philosophy for Multiprogramming Systems,»
Commun. of the ACM, vol. 11, pp. 347—360, May 1968.
72. Lampson, B.W.: «А Note on the Confinement Problem,» Commun. of the ACM,
vol. 10, pp. 613-615, Oct. 1973.
73. Lampson, B.W.: «Hints for Computer System Design,» IEEE Software, vol. 1,
pp. 11-28, Jan. 1984.
74. Ledin, G., Jr.: «Not Teaching Viruses and Worms is Harmful,» Commun. of the
ACM, vol. 48, p. 144, Jan. 2005.
75. Leschke, T.: «Achieving Speed and Flexibility by Separating Management from
Protection: Embracing the Exokernel Operating System,» Operating Systems
Review, vol. 38, pp. 5—19, Oct. 2004.
76. Levine, G.N.: «Defining Deadlocks,» Operating Systems Review vol. 37, pp. 54—64,
Jan. 2003a.
77. Levine, G.N.: «Defining Deadlock with Fungible Resources,» Operating Systems
Review, vol. 37, pp. 5—11, July 2003b.
78. Levine, G.N.: «The Classification of Deadlock Prevention and Avoidance is
Erroneous,» Operating Systems Review, vol. 39, 47—50, April 2005.
79. Lewine, D.: POSIX Programmer’s Guide, Sebastopol, CA: O’Reilly & Associates,
1991.
80. Li, K., and Hudak, P.: «Memory Coherence in Shared Virtual Memory Systems,»
ACM Trans, on Computer Systems, vol. 7, pp. 321—359, Nov. 1989.
81. Linde, R.R.: «Operating System Penetration,» Proc. AFIPS National Computer
Conf., AFIPS, pp. 361-368, 1975.
680 Глава 6. Библиография
82. Lions, J.: Lions’ Commentary on Unix 6th Edition, with Source Code, San Jose,
CA: Peer-to-Peer Communications, 1996.
83. Marsh, B.D., Scott, M.L., Leblanc, TJ., and Markatos, E.P.: «First-Class User-
Level Threads,» Proc. 13th Symp. on Oper. Syst. Prim, ACM, pp. 110—121, 1991.
84. McHugh, J.A.M., and Deek, F.P.: «An Incentive System for Reducing Malware
Attacks,» Commun. of the ACM, vol. 48, pp. 94—99, June 2005.
85. McKusick, M.K., Joy, W.N., Leffler, SJ., and Fabry, R.S.: «А Fast File System for
UNIX,» ACM Trans, on Computer Systems, vol. 2, pp. 181—197, Aug. 1984.
86. McKusick, M.K., and Neville-Neil, G.V.: The Design and Implementation of the
FreeBSD Operating System, Addison-Wesley: Boston, 2005.
87. Milo, D., Douglis, F., Paindaveine, Y, Wheeler, R., and Zhou, S.: «Process
Migration,» ACM Computing Surveys, vol. 32, pp. 241—299, July-Sept. 2000.
88. Milojicic, D.: «Operating Systems: Now and in the Future,» IEEE Concurrency,
vol. 7, pp. 12—21, Jan.-March 1999.
89. Moody, G.: Rebel Code Cambridge, MA: Perseus, 2001.
90. Morris, R., and Thompson, K.: «Password Security: A Case History,» Commun. of
the ACM, vol. 22, pp. 594-597, Nov. 1979.
91. Mullender, SJ., and Tanenbaum, A.S.: «Immediate Files,» Software — Practice
and Experience, vol. 14, pp. 365—368, April 1984.
92. Naughton, J.: A Brief History of the Future, Woodstock, NY: Overlook Books,
2000.
93. Nemeth, E., Snyder, G., Seebass, S., and Hein, T. R.: UNIX System Administation,
3rd Ed., Upper Saddle River, NJ, Prentice Hall, 2000.
94. Organick, E.I.: The Multics System, Cambridge, MA: M.I.T. Press, 1972.
95. Ostrand, TJ., Weyuker, EJ., and Bell, R.M.: «Where the Bugs Are,» Proc. 2004
ACM Symp. on Softw. Testing and Analysis, ACM, 86—96, 2004.
96. Peterson, G.L.: «Myths about the Mutual Exclusion Problem,» Information Proces
sing Letters, vol. 12, pp. 115—116, June 1981.
97. Prechelt, L.: «An Empirical Comparison of Seven Programming Languages,» IEEE
Computer, vol. 33, pp. 23—29, Oct. 2000.
98. Ray, D.S., and Ray, EJ.: Visual Quickstart Guide: UNIX, 2nd Ed., Berkeley, CA:
Peachpit Press, 2003.
99. Rosenblum, M., and Ousterhout, J.K.: «The Design and Implementation of a Log-
Structured File System,» Proc. 13th Symp. on Oper. Syst. Prin., ACM, pp. 1—15,
1991.
100. Russinovich, M.E., and Solomon, D.A.: Microsoft Windows Internals, 4th Ed.,
Redmond, WA: Microsoft Press, 2005.
101. Saltzer, J.H.: «Protection and Control of Information Sharing in MULTICS,»
Commun. of the ACM, vol. 17, pp. 388—402, July 1974.
102. Saltzer, J.H., and Schroeder, M.D.: «The Protection of Information in Computer
Systems,» Proc. IEEE, vol. 63, pp. 1278—1308, Sept. 1975.
6.2. Алфавитный список литературы 681
122. Uhlig, R., Nagle, D., Stanley, T, Mudge, T., Secrest, S., and Brown, R: «Design
Tradeoffs for Software-Managed TLBs,» ACM Trans, on Computer Systems, vol. 12,
pp. 175-205, Aug. 1994.
123. Uppuluri, P., Joshi, U., and Ray, A.: «Preventing Race Condition Attacks on File
Systems,» Proc. 2005 ACM Symp. on Applied Computing, ACM, pp. 346—353,
2005.
124. Vahalia, U.: UNIX Internals — The New Frontiers, 2nd Ed., Upper Saddle River,
NJ: Prentice Hall, 1996.
125. Vogels, W.: «File System Usage in Windows NT 4.0,» Proc. ACM Symp. on
Operating System Principles, ACM, pp. 93—109, 1999.
126. Waldspurger, C.A., and Weihl, W.E.: «Lottery Scheduling: Flexible Proportional-
Share Resource Management,» Proc. First Symp. on Oper. Syst. Design and
Implementation, USENIX, pp. 1—11, 1994.
127. Weiss, A.: «Spyware Be Gone,» NetWorker, vol. 9, pp. 18—25, March 2005.
128. Wilkes, J., Golding, R., Staelin, C, and Sullivan, T.: «The HP AutoRAID Hierarchical
Storage System,» ACM Trans, on Computer Systems, vol. 14, pp. 108—136,
Feb. 1996.
129. Wulf, W.A., Cohen, E.S., Corwin, W.M., Jones, A.K., Levin, R., Pierson, C., and
Pollack, FJ.: «HYDRA: The Kernel of a Multiprocessor Operating System,» Com
mun. of the ACM, vol. 17, pp. 337—345, June 1974.
130. Yang, J., Twohey, P., Engler, D. and Musuvathi, M.: «Using Model Checking to
Find Serious File System Errors,» Proc. Sixth Symp. on Oper. Syst. Design and
Implementation, USENIX, 2004.
131. Zekauskas, MJ., Sawdon, W.A., and Bershad, B.N.: «Software Write Detection for
a Distributed Shared Memory,» Proc. First Symp. on Oper. Syst. Design and
Implementation, USENIX, pp. 87—100, 1994.
132. Zwicky, E.D.: «Torture-Testing Backup and Archive Programs: Things You Ought
to Know but Probably Would Rather Not,» Prof. Fifth Conf, on Large Installation
Systems Admin., USENIX, pp. 181-190, 1991.
Приложение А
Установка MINIX 3
В данном приложении рассматриваются вопросы установки операционной систе
мы MINX 3. Для полной установки требуются компьютер с процессором Pentium
(или совместимым), не менее 16 Мбайт оперативной памяти, 1 Гбайт свободного
дискового пространства, CD-ROM с интерфейсом IDE и жесткий диск с ин
терфейсом IDE. Минимальная установка (без исходных файлов команд) требует
8 Мбайт оперативной памяти и 50 Мбайт дискового пространства. Поддержка
Serial АТА, USB и SCSI-дисков в настоящий момент отсутствует. За информа
цией о CD-ROM с интерфейсом USB обращайтесь на сайт www.minix3.org.
А. 1. Подготовка к установке
Если в вашем распоряжении имеется компакт-диск (например, приложенный
к этой книге), вы можете пропустить шаги 1 и 2, однако желательно осведомить
ся о наличии более новой версии операционной системы на сайте www.minix3.org.
Если вы хотите использовать MINIX 3 на симуляторе, сначала обратитесь к пунк
ту А.5. Если вы не располагаете IDE-устройством CD-ROM, получите специаль
ный загрузочный USB-образ CD-ROM или воспользуйтесь симулятором.
1. Загрузка образа компакт-диска MINIX 3 из Интернета.
Загрузите образ компакт-диска MINIX 3 с веб-сайта www.minix3.org.
2. Создание загрузочного компакт-диска MINIX 3.
Разархивируйте загруженный файл. Вы получите поразрядный файл образа
компакт-диска с расширением .iso и данное руководство. Запишите его на ком
пакт-диск. Это и будет загрузочный компакт-диск.
Если вы пользуетесь программой Easy CD Creator 5, в меню File (Файл) выбе
рите команду Record CD from CD image (Записать CD из образа) и в появив
шемся диалоговом окне измените расширение файлов с cif на iso.
При использовании Nero Express 5 выберите команду Disk Image or Saved Project
(Образ диска или сохраненный проект) и измените тип на Image Files (Файлы
образов), далее выберите файл образа и щелкните на кнопке Open (Открыть).
Выберите устройство CD-RW и щелкните на кнопке Next (Далее).
684 Приложение А. Установка MINIX 3
чтобы оставить место под MINIX. Во всех остальных случаях читайте упомя
нутое интерактивное руководство по адресу www.minix3.org/doc/partitions.html.
Если размер вашего диска превышает 128 Гбайт, раздел MINIX 3 должен
быть полностью размещен в первых 128 гигабайтах (из-за механизма адреса
ции дисковых блоков).
ВНИМАНИЕ -------------------------------------------
Если вы сделаете ошибку во время создания раздела, то можете потерять данные надиске,
поэтому сохраните их на CD или DVD перед началом установки. Разбиение на разделы тре
бует большой осторожности по причине опасности потери данных.
А. 2. Загрузка
На данном этапе у вас должен иметься некоторый объем свободного пространст
ва на вашем диске. Если вы еще не освободили требуемого пространства, сделай
те это сейчас. Раздел, предназначенный для MINIX 3, должен существовать.
1. Загрузка с CD-ROM.
Вставьте CD-ROM в накопитель и загрузите с него компьютер. Если компью
тер имеет не менеС 16 Мбайт оперативной памяти, выберите вариант Regular
(Обычная загрузка), если есть только 8 Мбайт — вариант Small (Компактная
загрузка). Если компьютер загружается с жесткого диска вместо CD-ROM,
запустите его снова, войдите в программу настройки BIOS и установите та
кой порядок загрузки, при котором сначала идет обращение к CD-ROM, а по
том к жесткому диску.
2. Вход в качестве пользователя root.
Когда появится запрос на вход в систему, войдите как пользователь root.
После успешного входа вы увидите приглашение оболочки (#). С этого мо
мента вы работаете в полнофункциональной операционной системе MINIX 3.
Если вы введете следующую команду, то сможете увидеть, какое программ
ное обеспечение доступно:
Is /usr/bin/ 1 more
Нажмите пробел для прокрутки списка. Чтобы узнать, что делает программа
foo, наберите команду man foo. Страницы руководств также доступны по
адресу http://www.minix3.org/manpages/.
3. Запуск сценария установки.
Для запуска сценария установки MINIX 3 на жесткий диск введите команду
setup. После этой и всех последующих команд не забудьте нажимать клавишу
Enter. Когда сценарий установки выведет на экран строки с текстом, нажмите
клавишу Enter для продолжения. Если экран внезапно погаснет, нажмите кла
виши Ctrl+F3 для «программной прокрутки» (может понадобиться только на
очень старых компьютерах). Обозначение Ctrl+клавиша указывает на то, что
нужно нажать клавишу Ctrl и, удерживая ее, нажать указанную клавишу.
686 Приложение А. Установка MINIX 3
Для первых двух вариантов введите номер раздела. Для третьего варианта
введите команду delete, а при появлении запроса введите номер раздела.
Этот раздел запишется заново, а его предыдущее содержимое будет поте
ряно навсегда.
3) Confirm your choices (Подтвердите свой выбор).
В данный момент мы достигли того этапа, после выполнения которого
«пути назад» уже нет. Появится запрос о том, желаете ли продолжать.
Если да, то все данные в выбранной области будут потеряны. Если вы увере
ны, введите команду yes и нажмите клавишу Enter. Для выхода из сцена
рия установки без изменения таблицы разделов нажмите клавиши Ctrl+C.
5. Reinstall Choice (Переустановка).
Если вы выбрали существующий раздел MINIX 3, на этом шаге вам будет
предоставлен выбор между полной установкой (Full install), которая сотрет все
на разделе, и переустановкой (Reinstall), которая не затронет ваш существую
щий раздел /home. Это означает, что вы можете поместить свои персональ
ные файлы в /home и заменить старую систему новой версией MINIX 3 без
потери своих файлов.
6. Select the size of /home (Выберите размер/home).
Выбранный раздел будет разделен на три подраздела: root, /usr и /home.
Последний предназначен для ваших персональных файлов. Определитесь,
сколько дискового пространства должно быть выделено для ваших файлов.
Выбор нужно будет подтвердить.
7. Select a block size (Выбор размера блока).
Поддерживаются блоки размерами 1, 2, 4 и 8 Кбайт, но для использования
размера более 4 Кбайт нужно изменить константу и перекомпилировать систе
му. Если объем оперативной памяти составляет 16 Мбайт или более, выберите
стандартное значение (4 Кбайт); в противном случае задайте 1 Кбайт.
8. Wait for bad block detection (Проверка поврежденных блоков).
Сценарий установки будет сканировать каждый раздел для поиска повреж
денных блоков. Это может занять несколько минут, возможно 10 минут и бо
лее на больших разделах; проявите терпение. Если вы абсолютно уверены,
что неисправных блоков нет, можно прекратить сканирование любого из раз
делов, нажав клавиши Ctrl+C.
9. Wait for files to be copied (Копирование файлов).
Когда закончится сканирование, файлы будут автоматически скопированы
с CD-ROM на жесткий диск. Вы увидите каждый копируемый файл. Когда
копирование завершится, система MINIX 3 окажется установленной. Вы
ключите систему, набрав команду shutdown. Всегда останавливайте MINIX 3
этим способом для предотвращения потери данных, так как MINIX 3 хра
нит некоторые файлы на виртуальном диске и копирует их на жесткий диск
только при завершении работы.
688 Приложение А. Установка MINIX 3
А.4. Тестирование
В этом разделе рассказывается, как проверить установленную систему, заново
собрать систему после модификации и затем загрузить ее. Для начала загрузите
вашу новую систему MINIX 3. К примеру, если вы используете контроллер О,
диск 0, раздел 3, введите команду boot c0d0p3 и войдите как пользователь root.
По некоторым причинам номер диска, видимый BIOS (и используемый монито
ром загрузки), может не совпадать с применяемым MINIX 3. Сначала попробуйте
задействовать номер, предложенный сценарием установки. Это хороший момент
для создания пароля пользователя root (справку вы можете получить с помо
щью команды man passwd).
1. Компиляция набора тестов.
Для проверки MINIX 3 наберите в командной строке следующую пару команд:
cd /usr/src/test
make
«голом» компьютере. Для этой цели имеется ряд виртуальных машин, симулято
ров и эмуляторов. Вот наиболее популярные:
♦ VMware (www.vmware.com);
+ Bochs (www.bochs.org);
+ QEMU (www.qemu.org).
Ознакомьтесь с соответствующей документацией. Запуск программы на симуля
торе аналогичен ее запуску на реальном компьютере, поэтому следует вернуться
к пункту АЛ, получить компакт-диск с последней версией операционной систе
мы и продолжить работу согласно данному руководству.
Приложение Б
Список файлов MINIX 3
на компакт-диске
Заголовочные файлы
00000 include/ansi.h
00200 include/errno .h
00900 include/fcntl.h
00100 include/limits.h
00700 include/signal.h
00600 include/string.h
01000 include/termios.h
01300 include/timers.h
00400 include/unistd.h
04400 include/ibm/interrupt.h
04300 include/ibm/portio.h
04500 include/ibm/ports.h
03500 include/minix/callnr.h
03600 include/minix/com.h
02300 include/minix/config.h
02600 include/minix/const.h
04100 include/minix/devio.h
04200 include/minix/dmap.h
02200 include/minix/ioct1.h
03000 include/minix/ipc.h
02500 include/minix/sys ! config.h
03200 include/minix/syslib.h
03400 include/minix/sysutil.h
02800 include/minix/type.h
01800 include/sys/dir.h
02100 include/sys/ioc ! disk.h
02000 include/sys/ioctl.h
01600 include/sys/sigcontext.h
01700 include/sys/stat.h
01400 include/sys/types.h
01900 include/sys/wait.h
Драйверы
10800 drivers/drivers.h
12100 drivers/at ! wini/at 1 wini.c
12000 drivers/at i wini/at ’ wini.h
692 Приложение Б. Список файлов MINIX 3 на компакт-диске
11000 drivers/libdriver/driver.с
10800 drivers/libdriver/driver.h
11400 drivers/libdriver/drvlib.с
10900 drivers/libdriver/drvlib.h
11600 drivers/memory/memory.с
15900 drivers/tty/console.c
15200 drivers/tty/keyboard.c
13600 drivers/tty/tty.c
13400 drivers/tty/tty.h
Ядро
10400 kernel/clock.c
04700 kernel/config.h
04800 kernel/const.h
08000 kernel/exception.c
05300 kernel/glo.h
08100 kernel/i8259.c
05400kernel/ipc.h
04600 kernel/kernel.h
08700 kernel/klib.s
08800 kernel/klib386.s
07100 kernel/main.c
06200 kernel/mpx.s
06300 kernel/mpx386.s
05700 kernel/priv.h
07400 kernel/proc.c
05500 kernel/proc.h
08300 kernel/protect.c
05800 kernel/protect.h
05100 kernel/proto.h
05600 kernel/sconst.h
06900 kernel/start.c
09700 kernel/system.c
09600 kernel/system.h
10300 kernel/system/do ! exec.c
10200 kernel/system/do ! setalarm.c
06000 kernel/table.c
04900 kernel/type.h
09400 kernel/utility.c
Файловая система
21600 servers/fs/buf.h
22400 servers/fs/cache.c
21000 servers/fs/const.h
28300 servers/fs/device.c
28100 servers/fs/dmap.c
21700 servers/fs/file.h
23700 servers/fs/filedes.c
21500 servers/fs/fproc.h
20900 servers/fs/fs.h
Менеджер процессов 693
21400 servers/fs/glo.h
22900 servers/fs/inode.с
21900 servers/fs/inode.h
27000 servers/fs/link.c
23800 servers/fs/lock.c
21800 servers/fs/lock.h
24000 servers/fs/main.c
26700 servers/fs/mount.c
24500 servers/fs/open.c
22000 servers/fs/param.h
26300 servers/fs/path.c
25900 servers/fs/pipe.c
27800 servers/fs/protect.c
21200 servers/fs/proto.h
25000 servers/fs/read.c
27500 servers/fs/stadir.c
23300 servers/fs/super.c
22100 servers/fs/super.h
22200 servers/fs/table.c
28800 servers/fs/time.c
21100 servers/fs/type.h
25600 servers/fs/write.c
Менеджер процессов
19300 servers/pm/break.с
17100 servers/pm/const.h
18700 servers/pm/exec.c
18400 servers/pm/forkexit .c
20400 servers/pm/getset.c
17500 servers/pm/glo.h
18000 servers/pm/main.c
20500 servers/pm/misc.c
17600 servers/pm/mproc.h
17700 servers/pm/param.h
17000 servers/pm/pm.h
17300 servers/pm/proto.h
19500 servers/pm/signal.c
17800 servers/pm/table.c
20300 servers/pm/time.c
20200 servers/pm/timers .c
17200 servers/pm/type.h
Алфавитный указатель
А H
ACL, 593 HTTP, 62
ANSI, 158,353
АТА, 329
1
В I/O, 19
IDE, 313
BIOS, 323,416
IDT, 190,217
BSD, 32
IEEE, 32
IOPL, 175
С IPC, 92
C-list, 595 IPL, 547
CMS, 70 IS, 140,397,660
cookie, 582 ISA, 19
CP/M, 33 i-узел, 61
CRC, 317
UI
C-Threads, 90
CTSS, 30
JVM, 71
D
DDOS, 581 L
DMA, 258 LAMP, 39
DOS, 34,581 LBA, 330
LBA, 48, 331
E LDT, 217,461
ECC, 255 LFS, 576
EIDE, 326 lock file, 295
EOF, 308 LRU, 445,611
LSI, 33
F
FAT, 551 M
FCFS, 317 M.I.T., 30
FIFO, 443 Mac OS X, 34
FMS, 27 MBR, 142,547
FORTRAN, 25 MFT, 418,553,558
FS, 139 MINIX, 36
FTP, 62
MMU, 427
Motif, 35
G MPI, 113
GDT, 190,216,461 MRU, 611
GID, 42 MS-DOS, 34
GUI, 34 MULTICS, 30
Алфавитный указатель 695
N
NFU, 447 абсолютный путь, 543
NRU, 442 аварийный сигнал, 41
NTFS, 532,558 автоконфигурирование, 258
автономный режим, 26
адаптер
О Ethernet, 292
OS/360, 28 ввода-вывода, 323
понятие, 254
сетевой, 153
Р адрес
PFF, 453 виртуальный, 427
PID, 50, 145 линейный, 463
PM, 139,467 физический, 176
POSIX, 32 адресация блоков
PPID, 145 линейная, 330
логическая, 314
PSW, 175,419
адресное пространство, 40
P-Threads, 90 активное ожидание, 97, 257
активный раздел, 142
R алгоритм
wsclock, 451
RAID, 315
банкира
RAM, 414
для нескольких видов ресурсов, 283
ROM, 34,323,414 для одного вида ресурсов, 281
RS, 140,656 быстрого соответствия, 426
второго шанса, 444
s замещения страниц, 440
наилучшего соответствия, 425
SATA, 327
наихудшего соответствия, 425
SLED, 315 первого соответствия, 424
SP, 67 планирования, 118
SPOOL, 30 вытесняющий, 120
SSF, 318 карусельного, 128
System V, 32 невытесняющий, 120
приоритетного, 129
циклического, 128
T следующего соответствия, 424
TLB, 436 старения, 447
TSS, 196,217 часов, 445
элеваторный, 318
u аппаратная прокрутка, 365
аппаратное прерывание, 149
UART, 343 архивация
UID, 42 инкрементная, 565
UNIX, 32 полная, 565
USB, 297 архитектура
UTC, 235 компьютера, 21
набора команд, 19
теговая, 596
w асинхронная передача, 262
wsclock, 451 асинхронное событие, 240
ассоциативная память, 436
атака
X отказа в обслуживании, 579, 581
X Windows, 34 с черного хода, 584
696 Алфавитный указатель
демон защита
печати, 93, 268 от дурака, 537
понятие, 81, 141 от несанкционированного доступа, 578
дескриптор понятие, 578
глобальный, 190, 216 защищенный режим, 168
локальный, 216 злоумышленник, 580
прерывания, 87, 190 зомби, 479
сегмента, 471
файла, 44, 616
шлюза прерывания, 196 и
джиттер, 124 идентификатор
диск группы, 42
виртуальный, 142 пользователя, 42
загрузочный, 142 процесса, 50
добавление соли, 587 иерархия памяти, 414
добровольная блокировка файлов, 617 имя
домен защиты, 591 пути, 43
доступ файла, 43
последовательный, 537 инверсия приоритета, 100
произвольный, 537 инвертированная таблица страниц, 438
доступность системы, 579 индексный узел, 61, 552
дочерний процесс, 41 инициализируемая переменная, 177
драйвер устройства, 139, 254, 263 инкрементная архивация, 565
дружественный интерфейс, 34 инкрементная резервная копия, 565
интерпретатор команд, 40
Е интерфейс
единообразное именование, 261 графический, 34
дружественный, 34
ж жестких дисков с интегрированной
электроникой, 325
жесткая связь, 546, 554 командной строки, 34
жесткая система реального времени, 134 передачи сообщений, ИЗ
жидкокристаллический дисплей, 341 системного вызова, 32
информационный сервер, 140, 397
3 исключение, 149, 205
зависание, 115
заголовок
процедуры, 536
к
канал, 45
сектора, 255
ввода-вывода, 255
файла, 192, 475, 535
секретный, 599
заголовочный файл, 157
канонический режим, 345
загрузочный блок, 185, 547, 604
загрузочный диск, 142 карта
загрузочный образ, 142, 186, 498 активных сигналов, 148
задание активных уведомлений, 148
программное, 25 источников прерываний, 148
с наименьшим временем завершения, 126 клавиш, 368
системное, 138, 222 карусельное планирование, 128
таймерное, 138 каталог, 42, 541
замещение страниц, 427 корневой, 43
глобальное, 451 рабочий, 43, 544
локальное, 451 спулера, 93
опережающее, 450 спулинга, 268
по запросу, 449 страничный, 463
запрос-отзыв, 588 текущий, 544
зарезервированный суффикс, 162 квант, 128
698 Алфавитный указатель
о переменная
инициализируемая, 177
оболочка, 46 условная, 107
обработка ошибок, 261 перехват
обработчик исключения, 53
прерываний, 215 клавиатурного ввода, 581
сигнала, 484 сигнала, 484
образ периодические события, 134
загрузочный, 142, 186, 498 пиксел, 341
памяти, 40 планирование
системный, 186 в системах реального времени, 134
общие права, 597 вытесняющее, 120
общий код, 470, 476 гарантированное, 132
объединение пространств данных и кода, 468 карусельное, 128
объект, 593 лотерейное, 132
объявление, 158 механизм, 135
оверлей, 426 невытесняющее, 120
ограничительный регистр, 419 политика, 135
одноразовый пароль, 587 приоритетное, 129
однородный ресурс, 271 справедливое, 133
операционная система, 18 циклическое, 128
многоуровневая, 67 планировщик, 118
монолитная, 65 допуска, 126
распределенная, 35 памяти, 127
сетевая, 35 процессора, 127
опережающее замещение страниц, 450 планируемая система реального времени, 134
определение функции, 158 поврежденный блок, 320
опрос, 257 подкачка, 29
отказ в обслуживании, 579, 581 подтверждение приема, 111
открытый исходный код, 39 поиск с перекрытием, 314
отладочный дамп, 179 поколения компьютеров
относительный путь, 544 второе, 25
ошибка отсутствия страницы, 429 первое, 24
четвертое, 33
п политика планирования, 135
полное имя файла, 43
пакетная обработка, 25 пользовательский режим, 20, 139
палмтоп, 415 порт ввода-вывода, 256
память последовательный доступ, 537
ассоциативная, 436 постоянная память, 34
виртуальная, 420, 426 почтовый ящик, 112
иерархия, 414 право доступа, 591
постоянная, 34 преамбула
сжатие, 420 битового потока, 255
уплотнение, 420 управляющей последовательности, 366
папка, 541 прерывание, 195, 215
параметр загрузки, 186, 325 аппаратное, 149
пароль, 587 программное, 149
первым пришел — первым обслужен, 124 префикс расширенных клавиш, 394
переадресация, 418 префиксный символ, 349
передача приватность, 579
асинхронная, 262 примитив
синхронная, 262 взаимодействия между процессами, 223
сообщений, 110 сообщения, 223
переключение принцип наименьшего уровня привилегий, 598
контекста, 128, 177 принципал, 593
процессов, 128 приоритетное планирование, 129
700 Алфавитный указатель
проблема
изоляции, 599
инверсии приоритета, 100 рабочая станция
обедающих философов, ИЗ бездисковая, 187
ограниченности буфера, 101 рабочий каталог, 43, 544
переадресации, 418 рабочий набор, 449
производителя и потребителя, 101 раздел
читателей и писателей, 117 активный, 142
пробуксовка, 449 главный, 548
программа диска, 62, 142
вредоносная, 580 логический, 548
начальной загрузки, 142 расширенный, 548
троянская, 582 фиксированный, 416
шпионская, 582 разделение
программная независимость от устройств, 261 времени, 30
программная прокрутка, 365 пространств данных и кода, 468
программное прерывание, 149 процессора, 248
программный поток, 89 разделяемое устройство, 262
произвольный доступ, 537 рандеву, ИЗ, 147
прокрутка, 365 раскладка клавиатуры, 368
аппаратная, 365 распределенная общая память, 456
программная, 365 распределенная операционная система, 35
промежуточное программное обеспечение, 32 распределенная система, 32
пропорциональность, 123 распределенный отказ в обслуживании, 581
пропускная способность, 122 расширение имени файла, 532
пространство расширенная машина, 22
адресное, 40 расширенный раздел, 548
дисковое, 37 расщепление данных, 316
оперативной памяти, 23 реальное время, 236
прототип функции, 158 регистр
процедура
базовый, 419
библиотечная, 544
ограничительный, 419
понятие, 151
сегментный, 471
процесс, 40
устройства, 19
блокировка, 84
режим
дочерний, 41
автономный, 26
завершение, 82
без обработки, 345
клиентский, 73
защищенный, 168
концепция, 78
легковесный, 89 канонический, 345
модель, 78 локальный, 351
ограниченный возможностями неканонический, 345
ввода-вывода, 119 однократного срабатывания, 234
ограниченный вычислительными пользовательский, 20, 139
возможностями, 119 с обработкой, 345
реализация, 86 с прерыванием, 352
серверный, 73 супервизора, 20
системный, 140 тактового меандра, 234
создание, 80 ядра, 20
состояние, 84 резервная копия
прямой доступ к памяти, 258 инкрементная, 565
псевдоним, 555 полная, 565
псевдопараллелизм, 78 ресурс, 270
псевдотерминал, 356 выгружаемый, 271
путь, 613 невыгружаемый, 271
абсолютный, 543 однородный, 271
относительный, 544 роль, 594
Алфавитный указатель 701
ф Я
файл, 530 ядро
атрибуты, 538 MINIX, 138
блокировки, 295 минимальное, 73
блочный, 535 язык
включаемый, 157 ассемблера, 19, 25
заголовочный, 157 машинный, 19
непосредственный, 559 программирования, 24
последовательного доступа, 537 ярлык, 555
Компакт-диск MINIX 3
Системные требования
Далее представлен список минимальных системных требований для установки
программного обеспечения, находящегося на данном компакт-диске.
Аппаратное обеспечение
Операционная система MINIX 3 предъявляет следующие требования к аппарат
ному обеспечению:
+ персональный компьютер с процессором Pentium или другим совместимым
с ним процессором;
♦ 16 Мбайт или более оперативной памяти;
♦ 200 Мбайт или более свободного пространства на жестком диске;
♦ драйвер CD-ROM с интерфейсом IDE;
♦ жесткий диск с интерфейсом IDE.
Диски с интерфейсами Serial АТА, USB и SCSI не поддерживаются. За альтер
нативными конфигурациями обратитесь на сайт http://www.minix3.org.
Программное обеспечение
MINIX 3 является операционной системой. Если вы желаете сохранить сущест
вующую операционную систему и данные (рекомендуется), реализовав на ком
пьютере возможность двойной загрузки, необходимо создать на жестком диске
раздел для MINIX 3. Вы можете воспользоваться следующими средствами:
♦ Partition Magic (http://www.powerquest.com/partitionmagic);
♦ Partition Resizer (http://www.zeleps.com).
Инструкции ищите на сайте http://www.minix3.org/partitions.html .
Установка
Установка может быть полностью выполнена без подключения к Интернету, однако
некоторые специальные документы доступны только на сайте http://www.minix3.org.
Исчерпывающие инструкции по установке имеются на компакт-диске в формате
Adobe Acrobat PDF.
Поддержка продукта
За дополнительной технической информацией о программном обеспечении MINIX,
содержащемся на данном диске, обратитесь на официальный веб-сайт MINIX по
адресу http://www.minix3.org.
Эндрю Таненбаум, Альберт Вудхалл
Операционные системы
Разработка и реализация (+CD)
Классика CS
3-е издание
Перевел с английского А. Кузнецов
Подписано в печать 26.12.06. Формат 70x100/16. Усл. п. л. 56,76. Тираж 3000. Заказ 3625.
ООО «Питер Пресс», 198206, Санкт-Петербург, Петергофское шоссе, 73, лит. А29.
Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 — литература учебная.
Отпечатано по технологии CtP в ОАО «Печатный двор» им. А. М. Горького.
197110, Санкт-Петербург, Чкаловский пр., 15.
Э. ТАНЕНБАУМ, А. ВУДХАЛЛ
ОПЕРАЦИОННЫЕ СИСТЕМЫ
Разработка и реализация
3-е издание
Operating Systems: Design and Implementation
CD-ROM ПРИЛАГАЕТСЯ
С^ППТЕР
Заказ книг:
197198, Санкт-Петербург, а/я 619
тел.: (812) 703-73-74, postbook@piter.com
61093, Харьков-93, а/я 9130
тел.: (057) 712-27-05, piter@kharkov.piter.com
www.piter.com — вся информация о книгах и веб-магазин