Добавлен: 29.10.2018
Просмотров: 48160
Скачиваний: 190
5.5. Часы
441
В качестве альтернативы прерываниям можно заставить приложение самостоятельно
вести опрос наступления ожидаемого события. Тем самым можно избежать прерыва-
ний, но при этом могут возникнуть существенные задержки, поскольку событие может
произойти сразу же после опроса и в этом случае придется ждать весь промежуток
времени до следующего опроса. В среднем задержка составляет половину интервала
времени между двумя опросами.
Сегодня задержка прерывания чуть ниже, чем на компьютерах 70-х годов прошлого
столетия. К примеру, на большинстве мини-компьютеров прерывание занимало четы-
ре такта шины: для помещения в стек счетчика команд и слова состояния программы
(PSW) и для загрузки новых счетчика команд и PSW. В наше время при работе с кон-
вейерами, блоками управления памятью, буферами TLB и кэшем издержки неизме-
римо выше. И со временем положение скорее ухудшится, чем улучшится, сводя на нет
увеличение тактовой частоты. К сожалению, для конкретных приложений не хотелось
бы ни издержек, связанных с прерываниями, ни задержек, связанных с опросами.
Программные таймеры
позволяют избежать прерываний. Вместо них, как только
управление по каким-то причинам передается ядру, непосредственно перед возвраще-
нием в режим пользователя осуществляется проверка значения фактического времени,
чтобы увидеть, не истекло ли время программного таймера. Если его время истекло,
осуществляется запланированное событие (например, передача пакета или проверка на-
личия входящего пакета), при этом нет надобности переходить в режим ядра, поскольку
система в нем и находится. После выполнения запланированной задачи программный
таймер перезапускается, чтобы снова быть задействованным. Нужно лишь скопировать
текущее значение часов в таймер и добавить к нему время истечения ожидания события.
Программные таймеры устанавливаются или сбрасываются с частотой передачи
управления ядру, осуществляемого по каким-то другим причинам. В число этих при-
чин входят:
системные вызовы;
отсутствие адресов в буфере TLB;
ошибки отсутствия страниц;
прерывания ввода-вывода;
отсутствие загруженности центрального процессора.
Чтобы определить, как часто происходят эти события, Арон и Дрюшель провели изме-
рения при нескольких вариантах загрузки центральных процессоров, включая полно-
стью загруженный веб-сервер, веб-сервер, имеющий фоновые задачи, ограниченные
по скорости вычислений, воспроизведение аудиопотока, получаемого из Интернета
в реальном масштабе времени, а также перекомпиляцию ядра UNIX. Средняя частота
вхождений в ядро изменялась от 2 до 18 мкс, при этом причиной около половины
этих вхождений были системные вызовы. Таким образом, в первом приближении за-
дача задействования программного таймера каждые 10 мкс была вполне выполнимой,
хотя и со случающимися время от времени нарушениями крайних сроков. Нечастые
опоздания на 10 мкс намного предпочтительнее, чем использование прерываний,
« съедающих» 35 % времени центрального процессора.
Разумеется, могут встречаться периоды без системных вызовов, отсутствия адресов
в TLB или ошибок отсутствия страниц, и в этом случае не будут задействоваться ни-
какие программные таймеры. Чтобы установить верхнюю планку таких интервалов,
442
Глава 5. Ввод и вывод информации
можно установить второй аппаратный таймер на задействованность, скажем, каждую
1 мс. Если приложение вытерпит режим всего в 1000 активаций в секунду в течение до-
вольно редких интервалов времени, то комбинация из программных таймеров и низко-
частотного аппаратного таймера может оказаться лучше, чем ввод-вывод, управляемый
только с помощью прерываний или только с помощью опросов.
5.6. Пользовательский интерфейс:
клавиатура, мышь, монитор
В качестве средства взаимодействия человека с компьютером используются клавиатура
и монитор (а временами и мышь). Хотя клавиатура и монитор с технической точки
зрения являются отдельными устройствами, они работают в тесном взаимодействии.
На больших универсальных компьютерах зачастую работает множество удаленных
пользователей, у каждого из которых в качестве отдельного модуля имеется устройство,
состоящее из клавиатуры и подключенного дисплея. Исторически такие устройства
называются терминалами. Люди часто используют этот термин, даже если речь идет
о клавиатурах и мониторах персональных компьютеров (главным образом из-за от-
сутствия более подходящего термина).
5.6.1. Программное обеспечение ввода информации
Пользователь вводит информацию в основном с помощью клавиатуры и мыши (иногда
с помощью сенсорных экранов), поэтому давайте их и рассмотрим. На персональных
компьютерах клавиатура содержит микропроцессор, который обычно через специали-
зированный последовательный порт обменивается данными с микросхемой контролле-
ра, расположенной на системной плате (хотя сейчас все чаще клавиатура подключается
к порту USB). Одно прерывание генерируется при нажатии клавиши, а второе — сра-
зу же, как только она будет отпущена. По каждому из этих клавиатурных прерываний
драйвер клавиатуры извлекает информацию о том, что именно произошло, пользуясь
при этом портом ввода-вывода, связанным с клавиатурой. Все остальное происходит
благодаря программному обеспечению и практически не зависит от аппаратуры.
Основной материал остальной части этого раздела будет лучше восприниматься,
если представлять себе ввод команд в окне оболочки (или в интерфейсе командной
строки). Именно так обычно и работают программисты. А графические интерфейсы
будут рассмотрены чуть позже. Некоторые устройства, в частности сенсорные экраны,
используются как для ввода, так и для вывода. Мы совершенно произвольно решили
рассмотреть их в разделе, посвященном устройствам вывода. Графический интерфейс
будет рассмотрен в данной главе чуть позже.
Программное обеспечение клавиатуры
Число, фигурирующее в порте ввода-вывода, является номером клавиши, который
называется скан-кодом (не следует его путать с кодом ASCII). У обычных клавиатур
имеется не более 128 клавиш, поэтому для представления номера клавиши хватает
семи битов. Восьмой бит устанавливается в 0, когда клавишу нажимают, и в 1, когда
ее отпускают. Состояние каждой клавиши (нижнее или верхнее ее положение) дол-
жен отслеживать драйвер клавиатуры. Следовательно, роль оборудования сводится
5.6. Пользовательский интерфейс: клавиатура, мышь, монитор
443
к предоставлению прерывания нажатия и освобождения. Все остальное выполняется
программными средствами.
К примеру, когда нажата клавиша A, ее скан-код (30) помещается в регистр ввода-вы-
вода. Драйвер должен определить, в режиме какого регистра, верхнего или нижнего,
работает клавиатура, в каком именно сочетании нажата эта клавиша,
CTRL+A
,
ALT+A
,
CTRL+ALT+A
или каком-нибудь другом. Поскольку драйвер может различить, какая
клавиша была нажата, но еще не отпущена (например,
SHIFT
), у него вполне достаточно
информации для успешной работы. К примеру, такая последовательность работы на
клавиатуре: нажать
SHIFT
, нажать
A
, отпустить
A
, отпустить
SHIFT
— будет означать
A
в верхнем регистре. Но такая последовательность: нажать
SHIFT
, нажать
A
, отпустить
SHIFT
, отпустить
A
— также будет означать
A
в верхнем регистре. Хотя при таком
клавиатурном интерфейсе все возлагается на программное обеспечение, он имеет ис-
ключительно гибкий характер. К примеру, пользовательской программе может быть
интересно, где вводилась цифра, на верхнем ряду клавиатуры или на дополнительной
цифровой клавиатуре, расположенной сбоку. В принципе, драйвер может предоставить
и такую информацию.
Для драйвера могут быть приняты два основных подхода. Первый из них ограничивает
работу драйвера восприятием входящей информации и передачей ее на более высокий
уровень без всяких изменений. Программа чтения с клавиатуры получает необработан-
ную последовательность ASCII-кодов. (Предоставлять пользовательским программам
скан-коды было бы слишком примитивным решением, к тому же находящимся в силь-
ной зависимости от используемой клавиатуры
1
.)
Этот подход хорошо сочетается с потребностями таких сложных экранных редакто-
ров, как emacs, которые позволяют пользователю привязывать произвольное действие
к любым символу или последовательности символов. Но это означает, что если поль-
зователь наберет dste вместо date, а затем исправит ошибку, нажав три раза на клавишу
удаления (backspace) и набрав ate, и завершит все это вводом символа возврата каретки,
пользовательская программа получит все 11 введенных ASCII-кодов:
dste←←←ate CR
Подобная детализация нужна не всем программам. Зачастую им нужен просто ис-
правленный ввод, а не точная последовательность, в которой он был произведен. Это
наблюдение приводит ко второму подходу: драйвер обрабатывает все редактирование
внутри строки, а пользовательской программе предоставляются уже скорректирован-
ные строки. Первый подход является символьно-ориентированным, а второй — строч-
но-ориентированным. Первоначально они назывались соответственно режимом без
обработки
и режимом с обработкой. Для описания строчно-ориентированного под-
хода в стандарте POSIX используется менее образный термин канонический режим.
А термин неканонический режим является эквивалентом для режима без обработки,
хотя многие особенности его поведения могут быть изменены. POSIX-совместимые
системы предоставляют несколько библиотечных функций, поддерживающих выбор
обоих режимов и изменение многих параметров.
1
Однако в ряде случаев это необходимо. Некоторые операционные системы предоставляют
пользовательским программам такую возможность по их запросу. Проблема зависимости
от клавиатуры при этом решается стандартизацией значений скан-кодов клавиш, также
позволяющей использовать универсальные драйверы. — Примеч. ред.
444
Глава 5. Ввод и вывод информации
Если клавиатура находится в каноническом режиме (с обработкой), символы долж-
ны храниться до тех пор, пока не будет набрана вся строка, поскольку пользователь,
приступив к набору, может впоследствии принять решение об удалении части строки.
Даже если клавиатура находится в режиме без обработки, программа могла еще не за-
прашивать входные данные, поэтому символы должны храниться в буфере, позволяя
пользователю осуществлять упреждающий ввод. Для этих целей должен использо-
ваться либо специализированный буфер, либо буфер, выделенный из пула. В первом
случае на упреждающий ввод накладывается определенный лимит, а во втором — нет.
Наиболее четко необходимость буфера проявляется, когда пользователь осуществляет
ввод в окне оболочки (или в окне командной строки, если говорить о Windows) в то
время, когда предыдущая команда (например, на компиляцию) еще не завершена. По-
следовательно набираемые символы должны скапливаться в буфере, поскольку оболоч-
ка еще не готова их прочитать. Системных программистов, не дающих пользователям
выполнять предварительный ввод с клавиатуры, нужно окунать в смолу и обваливать
в перьях или, и того хуже, заставлять работать на их же собственных системах.
Хотя логически клавиатура и монитор являются отдельными устройствами, многие
пользователи привыкли к тому, чтобы набираемые ими символы появлялись на экране.
Этот процесс называется отображением (echoing)
1
.
Отображение усложняется тем, что программа может выводить на экран другую ин-
формацию как раз в то время, когда пользователь что-нибудь набирает на клавиатуре
(опять представьте, что вводите данные в окно оболочки). По крайней мере, драйвер
клавиатуры должен определить, куда помещать новый ввод, чтобы на него не накла-
дывался программный вывод.
Отображение ввода усложняется и тем, что при вводе более 80 символов их надо ото-
бражать в 80-символьных строках (или в строках другой длины). В зависимости от
приложения в данном случае можно применить перенос на следующую строку. Не-
которые драйверы просто урезают строку до 80 символов, отбрасывая все символы
после 80-й позиции.
Другой проблемой является обработка символа табуляции. Обычно именно драйвер
должен вычислять текущую позицию курсора, учитывая как вывод, осуществляемый
программой, так и вывод в процессе отображения ввода, и вычислять точное количе-
ство отображаемых пробелов.
Теперь рассмотрим проблему эквивалентности устройств. Логически в конце строки
текста одним из них нужен код возврата каретки, чтобы переместить курсор обратно на
первую позицию, и код перевода строки, чтобы перейти на новую строку. Требования
к пользователям ставить оба этих кода в конце каждой строки вряд ли было бы встре-
чено с восторгом. Поэтому драйвер устройства должен конвертировать все в формат,
используемый операционной системой, независимо от того, что именно было введено.
В UNIX код клавиши
ENTER
конвертируется для внутреннего запоминания в код
перевода строки, а в Windows он конвертируется в код возврата каретки, за которым
следует код перевода строки.
Если стандартным является хранение кода перевода строки (как в соглашении UNIX),
то коды возврата каретки (генерируемые при нажатии клавиши
ENTER
) должны быть
превращены в коды перевода строки. Если внутренний формат предусматривает хра-
1
Также встречается под названиями «эхопечать» или «печать эха». — Примеч. ред.
5.6. Пользовательский интерфейс: клавиатура, мышь, монитор
445
нение обоих кодов (как в соглашении Windows), то драйвер должны сгенерировать
код возврата строки при получении кода возврата каретки и код возврата каретки
при получении кода перевода строки. Независимо от используемого внутреннего со-
глашения монитор для отображения ввода может потребовать и код перевода строки,
и код возврата каретки, чтобы правильно обновлять экран. На многопользовательских
системах, например на больших универсальных машинах, у разных пользователей
могут быть разные типы терминалов, подключенных к машине, и драйвер клавиатуры
должен получать всевозможные комбинации кодов возврата каретки/перевода строки
и преобразовывать их во внутренний стандарт системы, а также выстраивать их таким
образом, чтобы отображение выполнялось правильно.
При работе в каноническом режиме некоторые вводимые символы имеют специальное
предназначение. В табл. 5.4 показаны все специальные символы, необходимые системе
POSIX. Подразумевается, что все они являются символами управления, не конфлик-
тующими с текстовым вводом или кодами, используемыми программами; все, кроме
последних двух, могут быть изменены программным способом.
Таблица 5.4. Символы, обрабатываемые особым образом в каноническом режиме
Символ
Имя в POSIX
Комментарий
CTRL+H
ERASE
Забивание одного символа
CTRL+U
KILL
Стирание всей набранной строки
CTRL+V
LNEXT
Буквальное интерпретирование следующего символа
CTRL+S
STOP
Остановка вывода
CTRL+Q
START
Запуск вывода
DEL
INTR
Прерывание процесса (SIGINT)
CTRL+\
QUIT
Принуждение к выводу дампа ядра (SIGQUIT)
CTRL+D
EOF
Вставка кода конца файла
CTRL+M
CR
Вставка кода возврата каретки (неизменяемый)
CTRL+J
NL
Вставка кода перевода строки (неизменяемый)
Символ ERASE позволяет пользователю стереть только что введенный символ. Обыч-
но для этого применяется клавиша удаления
Backspace
(
CTRL+H
). Он не добавляется
в очередь символов, а вместо этого удаляет из нее предыдущий символ. Чтобы удалить
предыдущий символ с экрана, он должен быть отображен в виде последовательности из
трех символов: забивания, пробела и забивания. Если предыдущий символ был симво-
лом табуляции, его удаление зависит от того, как он был обработан при наборе. Если он
немедленно был превращен в несколько пробелов, то для определения того, на сколько
символов следует вернуться, нужна дополнительная информация. Если в очереди
ввода сохранен сам символ табуляции, он может быть удален, а вся строка выведена
на экран заново. В большинстве систем при забивании будут всего лишь стираться
символы в текущей строке. Забивание не будет стирать возврат каретки и возвращать
курсор на предыдущую строку. Если пользователь заметит ошибку при наборе в са-
мом начале строки, ему чаще всего удобнее удалить всю строку и начать набор заново.
Символ KILL удаляет всю строку. Многие системы убирают стертую строку с экрана,
но к отображению ввода на некоторых старых системах добавляются возврат каретки
и перевод строки, поскольку некоторые пользователи предпочитают видеть прежний