
- •Операционные системы для программиста
- •Введение
- •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. Командные файлы и сценарии
- •Библиографический список
5.2. Событийно-управляемый ввод
Поведение современных программных систем, активно взаимодействующих с пользователем, определяется не столько последовательностью операторов в исходной программе, сколько внешними воздействиями на компьютер. Такими воздействиями являются нажатия клавиш клавиатуры, перемещение мыши и нажатие ее кнопок, а также более сложные действия, реализуемые обычно также с помощью мыши. Примером такого сложного действия является перетаскивание какого-то графического объекта на экране с помощью мыши (dragging).
Поведение же программ, написанных в классических традициях, определяет сам программист, он при написании таких программ сам решает, что на очередном шаге выполнения будет требовать и ожидать программа: данных от клавиатуры или информации от мыши.
Естественно первый из перечисленных подходов отвечает реальным событиям в реальном мире, порядок которых никому, в том числе программисту, не известен. Поэтому именно он и реализуется в современных программах. Такая реализация потребовала отказа от безоговорочного следования за идеей алгоритма (как последовательности действий предварительно и строго описанной). По существу она оказалась возможной на основе сложного аппаратно-программного механизма, встроенного во все современные компьютеры и называемого механизмом прерываний.
Этот механизм позволяет немедленно реагировать на внешние сигналы путем запуска специально подготовленных программ – обработчиков прерываний, причем выполнявшаяся в момент прерывания программа временно приостанавливается с сохранением всей информации, необходимой для будущего продолжения. После завершения обработки прерываний прерванная программа возобновляет свое выполнение с прерванного места.
Разработка программ обработчиков прерываний очень не простая задача и поэтому ее выполняют разработчики операционных систем. Остальным предоставляется неявно пользоваться этим механизмом. Его использование в прикладных программах основывается на специализированных структурах данных о внешних по отношению к программе событиях.
Само событие, в частности движение мыши, это явление материальной реальности и, чтобы сделать его доступным для программ, внутренние компоненты ОС строят специализированные описания событий, обычно называемые сообщениями (message) или для выразительности просто событиями (event). Для нас, как программных пользователей операционной системы, существенно, что какой-то ее компонент подготавливает такие специализированные структуры данных для программ, способных их использовать.
Для текстовой консоли Windows, по замыслу разработчиков, теоретически возможны сообщения от нажатия клавиши клавиатуры, от мыши, сообщения об изменении размера окна, о переходе активности к текущему окну или о потере такой активности. Свойство активности визуально отражается изменением цвета заголовка окна и содержательно состоит в том, что только активное окно получает данные от клавиатуры.
В Windows программа для текстового окна может запросить сообщение путем вызова системной функции ReadConsoleInput. Эта функция имеет прототип
BOOL ReadConsoleInput(HANDLE hConsInput,
INPUT_RECORD* buffer, DWORD len, DWORD* actlen).
Кроме хэндла для указания консольного буфера ввода (в частности хэндла стандартного файла ввода) эта функция содержит адрес буфера, который представляет собой в общем случае массив записей типа INPUT_RECORD для размещения некоторого числа записей сообщений ввода. Размер массива выбирается программистом. Размер этого массива записей задается при вызове в параметре len. В простейших случаях массив buffer состоит из единственного элемента – для размещения единственного очередного сообщения, и параметр len берется поэтому равным 1. В общем случае, когда при вызове функции задается len, не равное 1, следует в программе после обращения к ReadConsoleInput проверять, сколько записей о вводе было действительно получено (с помощью параметра actlen), и принимать соответствующие действия с учетом этого фактического значения. Заметим, что функция ReadConsoleInput возвращает управление в вызвавшую ее программу только после появления сообщения о вводе. До этого момента вычислительный процесс выполнения программы, содержащей такую функцию, приостановлен (блокирован).
Сообщения как структуры данных типа INPUT_RECORD, получаемые от этой функции, имеют довольно сложное строение. Это строение описывается в заголовочном файле следующим образом:
typedef struct _INPUT_RECORD {
WORD EventType;
union {
KEY_EVENT_RECORD KeyEvent;
MOUSE_EVENT_RECORD MouseEvent;
WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
MENU_EVENT_RECORD MenuEvent;
FOCUS_EVENT_RECORD FocusEvent;
} Event;
} INPUT_RECORD, *PINPUT_RECORD;
Ключевым полем в этой записи является тип события EventType, его значения задаются предопределенными константами, описанными как
// EventType flags:
#define KEY_EVENT 0x0001 // Event contains key event record
#define MOUSE_EVENT 0x0002 // Event contains mouse event record
#define WINDOW_BUFFER_SIZE_EVENT 0x0004
// Event contains window change event record
#define MENU_EVENT 0x0008 // Event contains menu event record
#define FOCUS_EVENT 0x0010 // event contains focus change
Это ключевое поле EventType своим значением определяет более детальное строение сообщения. Если его значение равно KEY_EVENT, то на самом деле в этой универсальной структуре вся остальная часть (обобщенно называемая Event) есть структура типа KEY_EVENT_RECORD. Если же значение типа равно MOUSE_EVENT, то остальная часть есть в действительности структура типа MOUSE_EVENT_RECORD. (Остальные типы сообщений и их структуры в данном изложении рассматриваться не будут.)
Поэтому типовое использование системной функции ReadConsoleInput может быть описано наиболее характерной схемой вида
. . .
ReadConsoleInput( hInput, &inpbuf, 1, &actlen);
if (inpbuf. EventType = = KEY_EVENT)
{обработка события от клавиатуры,
структура которого представляется в программе обозначением
ipnbuf.Event.KeyEvent }
if (inpbuf. EventType = = MOUSE_EVENT)
{обработка события от мыши,
структура которого представляется в программе обозначением
ipnbuf.Event.MouseEvent }
. . . ,
в которой предполагается, что использованные информационные объекты данных где-то раньше описаны как
HANDLE hInput;
INPUT_RECORD inpbuf;
DWORD actlen;
В общем случае рассмотренный фрагмент должен, как правило, находиться внутри цикла, который приводит к многократному запросу сообщений.
Конкретные внутренние поля типов данных для сообщений от клавиатуры и мыши будут рассмотрены несколько дальше.