
- •Операционные системы для программиста
- •Введение
- •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. Командные файлы и сценарии
- •Библиографический список
9.5. Средства группового ожидания
Множественные ожидания событий или освобождения общих ресурсов возникают, когда по существу решаемой задачи может оказаться необходимым ожидать не одного, а более чем одного события или ресурса. Например, продолжить выполнение можно только после освобождения ресурса R1 или ресурса R2. Если в программе вначале поставить функцию ожидания ресурса R1, а затем функцию ожидания ресурса R2, то при освобождении ресурса R2, когда можно было бы по условию задачи продолжать работу, нить процесса по-прежнему будет заблокирована на функции ожидания ресурса R1, который все еще занят. Поставив вызовы функций в обратном порядке – вначале ожидания ресурса R2, затем ресурса R1, попадаем в аналогичную ситуацию невыполнения условий задачи, когда ранее освободится ресурс R1, а ресурс R2 все еще будет занят. Без помощи операционной системы подобную задачу решить оказывается невозможным. Поэтому в состав стандартных системных функций введены функции множественного ожидания.
В Windows и OS/2 входят специальные функции множественного ожидания. В OS/2 эти функции базируются на понятии и типе данных семафоров множественного ожидания – muxwait semaphore, сокращенно обозначаемых в наименовании системных функций как MuxWaitSem.
В Windows для множественного ожидания предназначена универсальная функция WaitForMultipleObjects с прототипом
DWORD WaitForMultipleObjects(DWORD cObjects,
CONST HANDLE *phObjects,// address of object-handle array
BOOL fWaitAll, // wait flag
DWORD Timeout), // time-out interval in milliseconds,
где параметр cObjects для данного применения задает число семафоров в наборе, параметр phObjects – адрес массива хэндлов отдельных семафоров в наборе, параметр Timeout – время ожидания в миллисекундах или записывается символической константой INFINITE – для бесконечного ожидания. С помощью параметра fWaitAll определяется вариант ожидания – ожидать срабатывания всех семафоров в наборе (значение параметра для этого должно быть TRUE) или ожидание завершается при срабатывании хотя бы одного семафора в наборе (при значении FALSE этого параметра). Возвращаемые значения этой функции, равные сумме константы WAIT_OBJECT_0 и числового значения k, информируют программу, что ожидание было прекращено по причине срабатывания k-го семафора в наборе.
В операционной системе Unix не предусмотрено стандартных средств для множественного ожидания срабатывания набора mutex-семафоров или семафоров ожидания (условных переменных). Вместо этого присутствуют мощные программные средства, позволяющие строить произвольные наборы считающих семафоров, которые будут рассматриваться далее в п. 9.7.
9.6. Программные критические секции
Программный интерфейс новых ОС не только предоставляет сложные универсальные средства решения для системных проблем, как это было в более ранних ОС, но и ориентирован на средства, позволяющие программисту решать отдельные относительно частные задачи. Следует, впрочем, отметить что последние версии давно созданных ОС также включают системные средства, облегчающие программисту его программное взаимодействие с ОС.
Критические интервалы процесса и нити традиционно организуются с помощью семафоров, но в последних ОС для их построения введены дополнительные системные функции. Эти функции получили названия функций работы с критическими секциями.
В операционной системе Windows критические секции необходимо предварительно описывать в программе структуры данных для функций, применяющих критические секции, и использовать функции инициализации таких секций. Функция инициализации имеет прототип
VOID InitializeCriticalSection(CRITICAL_SECTION *pCriticalSection),
где параметр функции задает адрес структуры данных типа CRITICAL_SECTION. Поля этой структуры, как правило, явно не используются и не изменяются.
Вход в критическую секцию задается в программе функцией EnterCriticalSection, которая имеет прототип
VOID EnterCriticalSection(CRITICAL_SECTION *pCriticalSection),
а завершение критической секции программы должно указываться вызовом функции с прототипом
VOID LeaveCriticalSection(CRITICAL_SECTION *pCriticalSection).
После использования объект критической секции должен быть удален из системы явным вызовом функции
VOID DeleteCriticalSection(CRITICAL_SECTION *pCriticalSection).
Критические секции, построенные с помощью рассмотренных функций, могут быть вложенными. Следует отметить, что в Windows повторный вход в ту же критическую секцию функцией EnterCriticalSection не приводит к переходу задачи в состояние ожидания, но для выхода из критической секции после этого необходимо выполнить функцию LeaveCriticalSection для того же аргумента столько раз, сколько выполнялась функция входа в такую секцию.