
- •Операционные системы для программиста
- •Введение
- •1. Основные понятия
- •1.1. Понятие операционной системы
- •1.2. Системные соглашения для доступа к функциям ос
- •1.3. Особенности разработки программ в базовых ос
- •1.4. Командный интерфейс пользователя в ос
- •1.5. Информация об ошибках системной функции
- •2. Программный доступ к файловой системе
- •2.1. Понятия дескрипторов, идентификаторов и хэндлов
- •2.2. Ввод и вывод в стандартные файлы.
- •2.3. Базовые средства использования файлов
- •2.4. Многопользовательская блокировка файлов
- •2.5. Установка произвольной позиции в файле
- •3. Принципы построения ос
- •3.1. Модульная структура построения ос
- •3.2. Использование прерываний в ос
- •3.3. Управление системными ресурсами
- •3.4 Строение ядра операционной системы
- •3.5. Структура операционной системы типа Windows nt
- •4. Многофункциональный консольный вывод
- •4.1. Функции управления курсором
- •4.2. Многократный вывод символов и атрибутов
- •4.3. Вывод в произвольную позицию экрана
- •4.4. Ввод данных, размещенных предварительно на экране
- •5. Системные функции ввода для консольных устройств
- •5.1. Системные функции ввода текстовых строк
- •5.2. Событийно-управляемый ввод
- •5.3. Системные функции ввода с клавиатуры
- •5.4. Опрос ввода с клавиатуры в программе
- •5.5. Системные функции мыши для текстового режима
- •6. Файловые системы
- •6.1. Структуры файловых систем для пользователя
- •6.2. Методы распределения внешней памяти
- •6.3. Принципы построения файловых систем типа fat
- •6.4. Современные модификации файловой системы fat
- •6.5. Особенности построения файловой системы hpfs
- •6.6. Принципы построения файловой системы ntfs
- •6.7. Особенности строения файловых систем для Unix
- •6.8. Программный опрос файловой системы
- •7. Обеспечение множественности процессов
- •7.1. Основные понятия теории вычислительных процессов
- •7.2. Программное порождение процессов
- •7.3. Уничтожение процессов
- •7.4. Ожидание завершения процессов
- •8. Многопоточное функционирование ос
- •8.1. Понятие нити и связь Хе с процессом
- •8.2. Создание нитей (thread) в программе
- •8.3. Уничтожение нитей
- •8.4. Приостановка и повторный запуск нити
- •8.5. Ожидание завершения нити
- •9. Средства взаимодействия программных единиц
- •9.1. Абстрактные критические секции
- •9.2. Абстрактные семафоры
- •9.3. Семафоры взаимоисключения
- •9.4. Семафоры событий
- •9.5. Средства группового ожидания
- •9.6. Программные критические секции
- •9.7. Программные семафоры с внутренним счетчиком
- •10. Управление памятью
- •10.1. Виртуальная память
- •10.2. ЏодкРчка страниц для реализациШ виртуальной памяти
- •10.3. Системные функции распределения памяти
- •10.4. Совместное использование памяти
- •10.5. Отображение файлов в оперативную память
- •10.6. Динамически распределяемая память
- •11. Средства коммуникации процессов
- •11.1. Неименованные коммуникационные каналы Unix
- •11.2. Переназначение хэндлов для доступа к каналу
- •11.3. Неименованные каналы в Windows
- •11.4. Именованные каналы в Windows nt
- •11.5. Именованные каналы в Unix
- •12. Взаимодействие пользователя с ос
- •12.1. Интерфейсы операционных систем
- •12.2. Командные и операционные оболочки (shells)
- •12.3. Основные команды базовых операционных систем
- •12.4. Групповое выполнение и фоновый запуск команд
- •12.5. Стандартный ввод-вывод и конвейеры командной строки
- •12.6. Командные файлы и сценарии
- •Библиографический список
3. Принципы построения ос
3.1. Модульная структура построения ос
Построение такой сложной системы, как операционная, невозможно без использования принципа модульности. Этот принцип состоит в том, что сложная система при проектировании разбивается на множество частей, относительно мало связанных друг с другом и достаточно просто взаимодействующих друг с другом. Практическое разбиение проектируемой системы на такие части-модули больше искусство чем наука, но для достаточно сложных систем такой путь оказывается единственным, чтобы справиться с совокупной сложностью всей системы.
Проектирование программных систем составляет отдельную и очень важную область практического программирования и изучается обычно в курсах технологий программирования. Операционные системы – уникальные произведения человеческого ума и труда, но после того, как они уже созданы, к ним достаточно просто подходить со стороны их модульного строения. (Это строение в процессе разработки может изменяться, но после завершения разработки воспринимается как логическое и установившееся.)
Подобно строению большинства программных систем, строение операционных систем определяется в первую очередь их функциями. Основные функции ОС, рассматривавшиеся в гл. 1, наряду с другими, более частными, но необходимыми, вызывают разбиение ОС на следующие подсистемы, характерные для всех многопрограммных ОС.
Этими подсистемами являются:
файловые системы;
подсистема управления устройствами;
подсистема управления оперативной памятью;
подсистема управления процессами;
подсистема интерфейса с пользователем;
подсистема управления задачами и диспетчеризацией;
подсистема ведения времени и обслуживания таймеров.
Такое деление является самым грубым и приблизительным, но уже оно позволяет выделить достаточно различные и в значительной степени автономные части. Заметим, что кроме перечисленных основных частей в ОС, как правило, выделяют так называемое ядро ОС (OS kernel). Через ядро как через всеобщий диспетчер осуществляется передача управления от отдельных запросов на системные функции к частным подсистемам. Ядру обычно поручается и создание управляющих блоков для системных информационных объектов. Подробней понятие ядра будет рассматриваться в одном из следующих разделов.
3.2. Использование прерываний в ос
Построение всех операционных систем в конечном счете основывается на понятии прерывания и его реализации как работающего механизма в совокупности аппаратных и программных средств.
Детальное изучение механизма прерывания возможно только на нижнем уровне детализации программных действий, когда в таком описании используются особенности архитектуры, т.е. логического строения и функционирования аппаратуры компьютера, достаточных для программиста. (Практически архитектура - это точное описание границы между детальным программным и аппаратным обеспечением.) Такое детальное описание использует понятие машинных команд и регистров программиста. К сожалению, подобные описания требуют детализации, которая хорошо излагается только с помощью изобразительных средств языка ассемблера. Изучение последнего представляет само по себе немалый труд.
Поэтому подойдем с другой стороны, частично описательной, но в основном содержательно принципиальной, описывая особенности механизма прерывания с помощью аналогий и близких понятий.
Прерывание – это аппаратно-программное средство, которое предоставляет возможность вызова программ не по имени или указателю, а по числовому номеру. Читатели должны быть хорошо знакомы с использованием подпрограмм на языке Си или Паскаль. Подпрограмма некоторых действий описывается отдельно от места ее использования, а затем там, где следует выполнить действия обычной подпрограммы, на языке высокого уровня записывается ее вызов путем указания имени и, если нужно, фактических аргументов. Обобщенно это может быть описано на метаязыке в виде
<вызвать подпрограмму> имя подпрограммы
Заметим, что после трансляции программы вызовы подпрограмм могут преобразовываться в другую форму, которая явно или неявно использует указатель на место размещения подпрограммы, аналогично тому, как в языке Си применяется доступ к данным через указатель на них. При всей распространенности и привычности для программиста такой подход вызова подпрограмм неприменим для более простой электронной аппаратуры. Аппаратура, по сложности соизмеримая с простым микропроцессором, в лучшем случае может переслать куда-то небольшое число или сигнал. Заметим, что прерывания – это механизм, который предназначен в первую очередь для вызова подпрограмм сигналами непосредственно от аппаратуры.
Если вспомним назначение рассмотренных ранее хэндлов, являющихся как числовые значения небольшими числами, то легко сообразить, что тот же подход может быть использован для перехода от небольших чисел, посылаемых аппаратурой, к действительным значениям указателей (адресам) подпрограмм. Достаточно где-то в пределах операционной системы построить таблицы, в строки которых записать такие указатели на начало подпрограмм. Правда, дополнительно к таким таблицам нужно дать операционной системе какие-то средства по автоматическому преобразованию номеров вызова подпрограмм через эту таблицу. Эти автоматические средства вкладываются в аппаратуру современных процессоров (уже более 40 лет!) и называются механизмом прерываний.
В простейшем случае приказ запуска механизма прерывания записывается прямо в программе. Следует заметить, что никакие языки высокого уровня не позволяют записать непосредственно такой приказ, он допустим только на уровне машинных кодов и команд ассемблера. Подобный вызов называют программным прерыванием. В частности архитектура процессоров типа Intel на языке ассемблера записывает такой приказ в виде
INT номер
Программные прерывания – это как бы использование чужих по назначению средств в своих программистских целях. На самом деле это очень мощное и используемое внутри ОС средство, но к нему вернемся несколько позже.
Прерывания дают операционным системам неоценимый механизм для вмешательства реального времени и реальных внешних процессов в формализованный мир последовательной обработки информации.
В частности, нажатие клавиши клавиатуры может быть произведено пользователем в любой момент – и через долю секунды и через час, если он займется или отвлечется чем-то другим. В течение всего этого времени (достаточно большой продолжительности для компьютера) целесообразно поручить компьютеру делать что-то более существенное чем просто ждать нажатия на клавищу. Ему можно поручить выполнять другую программу, проигрывать аудизапись и т.п. Прерывание явилось тем средством, с помощью которого внешняя к процессору аппаратура запускает подпрограмму обслуживания событий, вызванных этой аппаратурой.
В состав всех современных компьютеров входит так называемый таймер, который через небольшие периодические интервалы времени посылает сигнал, вызывающий через механизм прерывания специальную подпрограмму обработки прерываний от таймера. Такие сигналы и вызовы обработки прерываний возникают многие десятки раз в секунду. Человек уже не может уследить за такой частотой появления сигналов, но компьютер между двумя последовательными такими сигналами выполняет многие миллионы внутренних операций и команд.
Для нас существенно, что при возникновении аппаратного прерывания оно может прийтись на любое место выполняемой в этот момент компьютерной программы. Получается, что выполняется обычная программа, которая не требует никаких собственных обращений к подпрограммам, и вдруг неожиданно для нее происходит переход на чужую подпрограмму обработки прерывания, не предусмотренную в выполняемой. Новичок скажет: "Ну и очень плохо, все испортится!". Ничего подобного! Подпрограмма обработки аппаратного прерывания выполнится, совершит заложенную в нее обработку (например, обновить изображение часов на экране) и "вернет управление" в место прерванной программы. При этом прерванная программа продолжится точно с того места, где она была прервана и притом точно с теми же данными, которые были получены в упомянутом месте. Как технически реализуется запоминание места прерываемой программы, возврат в прерванное место и восстановление ситуации с оперативно обрабатываемыми данными можно понять только на уровне архитектуры и изобразительных средств ассемблера. Для текущего пониманая существенно, что особенности технического механизма прерывания, кроме собственно перехода к подпрограмме обработчика, включают и средства восстановления ситуации для абсолютно точного возвращения ситуации на момент прерывания.
Механизм прерывания можно образно представить следующей картиной. Вы читаете книгу, и вдруг неожиданно раздается телефонный звонок (аналог сигнала от таймера или другой аппаратуры). Вы прерываете свое основное занятие, запомнив место, где приостановили чтение текста, и беретесь за телефонную трубку, а после завершения разговора возвращаетесь к прерванному месту. Заметим, что на основе подобной картины можно представить себе и "вложенные прерывания", когда среди такого телефонного разговора раздается звонок во входную дверь, и вы идете открывать или узнавать, что нужно звонившему, а затем возвращаетесь к прерванному разговору, после завершения последнего – к продолжению чтения.