введение_1 (1085732), страница 14
Текст из файла (страница 14)
81 games 70 memo 81 games 70 memo
40 test 59 f.c. 40 test 59 f.c.
38 prog1 70 note 38 prog1
а б
Рис. 1.20. Файловая система: до вызова mount (а); после вызова mount (б)
После вызова mount доступ к файлу на диске 0 можно получить, просто указав его путь из корневого или рабочего каталога, независимо от того, на каком диске он находится. В действительности второй, третий и четвертый диски тоже можно встроить в любое удобное место в дереве. Вызов mount позволяет объединить съемные носители в единую интегрированную файловую структуру, не заботясь о том, на каком из устройств фактически находится файл. Хотя в нашем примере рассматривались гибкие диски, жесткие диски или их части (часто называемые разделами (partition) или младшими устройствами (minor devices)) монтируются аналогично. Когда файловая система более не нужна, ее можно демонтировать с помощью системного вызова umount.
Разные системные вызовы
Существует, помимо описанных выше, еще множество системных вызовов. Сейчас мы рассмотрим только четыре из них. Вызов chdir изменяет текущий рабочий каталог. После вызова
chdiг("/usr/ast/test");
процедура открытия файла xyz откроет /usr/ast/test/xyz. Использование понятия рабочего каталога избавляет от необходимости постоянно набирать длинные абсолютные пути файлов.
В UNIX для каждого файла определен режимный код файла, иногда также называемый модой (mode), используемый для его защиты. Режимный код включает в себя биты чтения-записи-выполнения для владельцев, группы и других пользователей. Системный вызов chmod предоставляет возможность изменения режимного кода файла. Например, следующий вызов предоставит всем, кроме владельца, доступ к файлу только для чтения; владелец же сможет еще и выполнять файл:
chmod(“file", 0644);
Системный вызов kill позволяет пользователям и пользовательским процессам посылать сигналы. Если процесс готов принять определенный сигнал, то при его прибытии запускается обработчик сигналов. Если же процесс не готов обработать сигнал, тогда прибытие сигнала уничтожает процесс (отсюда произошло название вызова, kill в переводе означает «убивать, уничтожать»).
Стандартом POSIX определено несколько процедур, имеющих отношение ко времени. Например, time возвращает текущее время в секундах, причем нулем считается полночь 1 января 1970 года (имеется в виду начало дня, а не его конец). На компьютерах с 32-разрядными словами максимальная величина, которую может вернуть time, составляет 232-1 с (используется положительное целое число). Эта величина соответствует периоду немногим более 136 лет. Таким образом, в 2106 году 32-разрядные системы UNIX «сойдут с ума», имитируя знаменитую проблему 2000 года (Y2K). Если сейчас у вас 32-разрядная система UNIX, мы рекомендуем вам поменять ее на 64-разрядную, прежде чем наступит 2106 год.
Windows Win32 API
До сих пор мы концентрировали свое внимание в первую очередь на системе UNIX. Теперь самое время обратить внимание на Windows. В Windows и UNIX
фундаментально отличаются соответствующие модели программирования. Программы UNIX состоят из кода, который выполняет те или иные действия, обращаясь к системе с системными запросами для предоставления ему конкретных услуг. В противоположность этому программы в Windows обычно приводятся в действие событиями. Основной модуль программы ждет, когда произойдет какое-либо событие, затем вызывает процедуру для его обработки. Типичными событиями являются: нажатие клавиши мыши или клавиатуры, передвижение мыши или появление гибкого диска в дисководе. Затем обработчики, вызываемые для обработки события, переписывают содержимое экрана и внутреннее состояние программы. Все это ведет к совершенно отличному от UNIX стилю программирования, но поскольку наша книга посвящена функциям и структуре операционной системы, различные модели программирования не имеют к нам сейчас особого отношения.
Конечно, в Windows тоже есть системные вызовы. В UNIX вызовы почти один к одному идентичны библиотечным процедурам (вспомним read), используемым для обращения к системным вызовам. Другими словами, для каждого системного вызова существует одна библиотечная процедура, обычно с тем же названием, которая вызывается для обращения к нему. Это было показано на рис. 1.17. Кроме того, в стандарте POSIX всего лишь около 100 процедурных вызовов.
Ситуация в системе Windows совершенно иная. Во-первых, фактические системные вызовы и запускающиеся для их выполнения библиотечные вызовы полностью разделены. Корпорацией Microsoft определен набор процедур, называемый Win32 API (Application Program Interface — интерфейс прикладных программ). Предполагается, что программисты должны использовать его для вызова служб операционной системы. Этот интерфейс частично поддерживается всеми версиями Windows, начиная с Windows 95. Отделяя интерфейс от фактических системных вызовов, Microsoft поддерживает возможность изменения со временем действительных системных вызовов (даже от одной версии к другой), не делая при этом недействительными существующие программы. Ситуация на самом деле складывается неоднозначная, так как в Windows 2000 появилось много новых вызовов, ранее недоступных. В этом разделе Win32 означает интерфейс, поддерживаемый всеми версиями Windows.
Количество вызовов в Win32 API крайне велико, вызовы исчисляются тысячами. Кроме того, хотя многие из них являются системными, существенное число работает целиком в пространстве пользователя. Из-за этого в Windows невозможно понять, является ли вызов системным (то есть выполняемым ядром), или просто библиотечным вызовом, который обрабатывается в пространстве пользователя. В принципе вызов, считающийся системным в одной версии Windows, может выполняться в пользовательском пространстве в других версиях, и наоборот. При обсуждении системных вызовов Windows в этой книге мы будем использовать соответствующие процедуры Win32, поскольку Microsoft гарантирует, что они не будут меняться. Но стоит всегда помнить, что не все они являются настоящими системными вызовами (то есть прерываниями с переключением в режим ядра).
Другое различие заключается в том, что в UNIX графический интерфейс пользователя (например, X Windows или Motif) запускается целиком в пользовательском пространстве. Поэтому для вывода на экран достаточно системного вызова
write и несколько других незначительных вызовов. Конечно, существует достаточно большое количество обращений к X Windows и GUI, но они не являются системными вызовами в любом смысле этого слова.
В противоположность этому Win32 API имеет огромное количество вызовов для управления окнами, геометрическими фигурами, текстом, шрифтами, полосами прокрутки, диалоговыми окнами, пунктами меню и другими элементами графического интерфейса. В том случае, когда графическая подсистема запускается в режиме ядра (это верно для большинства версий Windows, но не для всех), вызовы являются системными; в противном случае вызовы являются только библиотечными. Должны ли мы обсуждать эти вызовы в книге или нет? Так как они на самом деле не связаны с функциями операционной системы, мы решили этого не делать, несмотря на то, что они выполняются ядром. Читателям, интересующимся Win32 API, мы рекомендуем обратиться к любой из множества книг по этому предмету.
Даже перечисление всех вызовов Win32 API выходит за рамки этой книги, поэтому мы ограничимся теми из них, которые приблизительно соответствуют функциональности вызовов UNIX, перечисленных в табл. 1.1. Эти вызовы показаны в табл. 1.2.
Таблица 1.2. Вызовы Win32 API, приблизительно соответствующие вызовам UNIX, перечисленным в табл. 1.1
U NIX Win32 Описание
Fork CreateProcess Создать новый процесс
Waitpid WaitForSingleObject Ждать завершения процесса
Execve (нет) CreateProcess=fork+execve
Exit ExitProcess Завершить выполнение
Open CreateFile Создать файл или открыть существующий файл
Close CloseHandle Закрыть файл
Read ReadFile Читать данные из файла
Write WriteFile Записать данные в файл
Lseek SetFilePointer Передвинуть указатель файла
Stat GetFileAttributesEx Получить различные атрибуты файла
Mkdir CreateDirectory Создать новый каталог
Rmdir RemoveDirectory Удалить пустой каталог
Link (нет) Win32 не поддерживает связи
Unlink DeleteFile Удалить существующий файл
Mount (нет) Win32 не поддерживает монтирование
Urnount (нет) Win32 не поддерживает монтирование
chdir SetCurrentDirectory Изменить текущую рабочую папку
chmod (нет) Win32 не поддерживает защиту файла (хотя NT поддерживает)
kill (нет) Win32 не поддерживает сигналы
time GetLocalTime Получить текущее время
Теперь кратко пройдемся по списку, представленному в табл. 1.2. Вызов CreateProcess создает новый процесс. Он комбинирует работу вызовов fork и execve
в UNIX. У этого вызова есть множество параметров, определяющих свойства созданных процессов. В Windows нет такой иерархии процессов, как в UNIX, поэтому здесь нет концепции родительских и дочерних процессов. После создания нового процесса «создатель» и «созданный» становятся равноправными. WaitForSingleObject используется для ожидания события. Существует множество событий, которые можно ждать. Если параметром указан процесс, тогда вызывающая программа ждет завершения определенного процесса. Завершение процесса выполняется с помощью вызова ExitProcess.
Следующие шесть вызовов оперируют файлами и функционально похожи на свои аналоги в UNIX, несмотря на то, что они имеют различные параметры и детали. Таким образом, файлы можно открывать, закрывать и читать практически так же, как в UNIX. Вызовы SetFilePointer и GetFileAttributesEx устанавливают позицию указателя и получают некоторые из атрибутов файла.
В Windows также есть каталоги, их создают и удаляют вызовы CreateDirectory и RemoveDirectory соответственно. Здесь тоже есть понятие текущего каталога, он задается с помощью SetCurrentDi rectory. Для получения текущего времени используется GetLocalTime.
В интерфейсе Win32 не существует связанных файлов, монтирования файловых систем, защиты файлов и сигналов, поэтому также нет и вызовов, соответствующих вызовам UNIX. Конечно, в Win32 есть огромное количество самых разных других вызовов, которых не имеет UNIX, особенно относящихся к управлению графическим интерфейсом. Однако система Windows 2000 имеет тщательно продуманную систему безопасности и, кроме того, поддерживает связи между файлами.
Стоит сделать еще одно, последнее замечание относительно Win32. Win32 не является полностью единообразным и последовательным интерфейсом. Причиной этого явилась необходимость обратной совместимости с ранее использовавшимся в Windows 3.x 16-разрядным интерфейсом.
Структура операционной системы
Теперь, когда мы уже видели, как выглядят операционные системы снаружи (то есть мы знакомы с программным интерфейсом), самое время заглянуть внутрь. Чтобы получить представление обо всем спектре возможных вариантов, в следующих разделах мы исследуем пять различных использующихся (или использовавшихся ранее) структур. Исследование это нельзя назвать всесторонним, здесь лишь рассматривается несколько моделей, применявшихся на практике в разных системах. Их пять — монолитные системы, многоуровневые системы, виртуальные машины, экзоядро и модель клиент-сервер.
Монолитные системы
В общем случае организация монолитной системы представляет собой «большой беспорядок». То есть структура как таковая отсутствует. Операционная система
написана в виде набора процедур, каждая из которых может вызывать другие, когда ей это нужно. При использовании такой техники каждая процедура системы имеет строго определенный интерфейс в терминах параметров и результатов, и каждая имеет возможность вызвать любую другую для выполнения некоторой необходимой для нее работы.
Для построения монолитной системы необходимо скомпилировать все отдельные процедуры, а затем связать их в единый объектный файл с помощью компоновщика. Здесь, по существу, полностью отсутствует сокрытие деталей реализации — каждая процедура видит любую другую процедуру (в отличие от структуры, содержащей модули, в которой большая часть информации является локальной для модуля и процедуры модуля можно вызвать только через специально определенные точки входа).
Однако даже такие монолитные системы могут иметь некоторую структуру. При обращении к системным вызовам, поддерживаемым операционной системой, параметры помещаются в строго определенные места — регистры или стек, после чего выполняется специальная команда прерывания, известная как вызов ядра или вызов супервизора. Эта команда переключает машину из режима пользователя в режим ядра и передает управление операционной системе, что видно на шаге б рис. 1.17. Затем операционная система проверяет параметры вызова, чтобы определить, какой системный вызов должен быть выполнен. После этого операционная система обращается к таблице как к массиву с номером системного вызова в качестве индекса. В k-m элементе таблицы содержится ссылка на процедуру обработки системного вызова k (шаг 7 на рис. 1.17). Такая организация операционной системы предполагает следующую структуру.