Главная страница Межпроцессное взаимодействие (состязание) Следующая на очереди - процедура setattr. Как вы могли видеть, она вызывается из dojoctl и devjoctl, чтобы изменить атрибуты терминального устройства, а также из do close, когда нужно сбросить атрибуты в значения по умолчанию. Эта функция всегда вызывается после того, как записываются новые данные в структуру termios, поскольку лишь изменения значений недостаточно. Если управляемое устройство переведено в неканонический режим, необходимо установить бит IN EOT у всех символов во входной очереди, как если бы они изначально были напечатаны в неканоническом режиме. Так как нельзя узнать, какой атрибут только что был изменен, невозможно и определить, следует ли устанавливать бит у символов в очереди. Поэтому проще все свести к одному случаю, нежели чем проверять содержимое очереди. Далее нас интересуют значения параметров MIN и TIME. В каноническом режиме tp->tty min всегда равно 1. Проверка выполняется в теле первого условного оператора после вызова unlock. В неканоническом режиме различные комбинации этих параметров позволяют реализовать четыре разных режима работы, показанных в табл. 3.10. В tp->tty min сначала записывается значение, переданное через tp->tty termiso.cc[VMIN], а после, при равенстве его нулю и при нулевом значении в tp->tty termiso.cc[VTIME], оно изменяется. Наконец, setattr обеспечивает, что вывод не останавливается, если отключено управление XON/XOFF, отправляет сигнал SIGHUP, если скорость передачи установлена в ноль, и делает косвенный вызов подпрограммы, адрес которой хранится в tp->tty ioctl, чтобы выполнить те действия, которые могут быть выполнены только на уровне устройства. Функция tty reply уже много раз упоминалась в предшествующих обсуждениях. Ее алгоритм прост, она формирует сообщение и отправляет его. Если по какой-то причине отправить сообщение не удалось, происходит сбой системы. Оставшиеся функции столь же просты. Функция sigchar заставляет менеджер памяти отправить сигнал. Если установлен флаг NOFLSH, очищается входная очередь - обнуляется счетчик полученных строк или символов, а указатели на конец и начало очереди становятся равными. Это действие по умолчанию. Флаг может быть также установлен, когда ожидается сигнал SIGHUP, чтобы продолжить ввод и вывод и после получения сигнала. Функция ttyjcancel очищает входную очередь, как это делает sigchar, а в дополнение вызывает аппаратно-специ-фичную функцию tp->ttyjcancel, с целью очистить буфер самого устройства при наличии такого буфера. Функция ttyjnit вызывается один раз на каждое устройство при запуске tty task. Она устанавливает значения по умолчанию. Изначально в поля tp->ttyjcancel, tp->tty ocancel, tp->ttyJoctl и tp->tty close записывается указатель на заглушку, tty devnop. Затем ttyjnit вызывает одну из специфичных для устройства функций инициализации, в зависимости от того, к какой категории относится терминал: консоль, последовательная линия или псевдотерминал. Эта функция сохраняет в полях структуры ссылки на реальные функции, специфичные для данного устройства. (Вспомните, что если в определенной категории не инициализировано ни одного устройства, соответствующий макрос трубит немедленный отбой, в результате чего не компилируется ни одна из частей кода неиспользуемых уст- ройств.) Затем вызов init scr инициализирует драйвер консоли и обращается к процедуре инициализации клавиатуры. Функция tty wakeup невелика, но играет очень важную роль в работе задачи терминала. Когда стартует обработчик прерываний часов, значение глобальной переменной tty timeout (файл glo.h) сравнивается с ее прошлым значением, скажем, на каждом такте таймера. Если новое значение меньше, вызывается функция tty wakeup. Значение tty timeout устанавливается в ноль обработчиками прерываний драйверов терминалов, поэтому после каждого прерывания от терминала обеспечивается вызов tty wakeup. Кроме того, как мы увидим далее, значение tty timeout изменяется подпрофаммой settimer, когда терминал в неканоническом режиме выполняет вызов read и требуется задержка. Когда tty wakeup получает управление, она прежде всего предотвращает дальнейшие вызовы tty wakeup, для чего записывает в tty timeout значение TIME NEVER, которое соответствует времени далеко в будущем. Затем функция сканирует список значений таймеров до тех пор, пока не находит таймер, время срабатывания которого позже текущего (список отсортирован так, что в его голове находится таймер с самым ранним срабатыванием). Найденное значение определяет момент следующего пробуждения, и оно заносится в tty timeout. Кроме того, tty wakeup сбрасывает переменную tp->tty min в О, благодаря чему следующее чтение с этого устройства будет выполнено, даже если не будет получено ни одного байта. Также для этого устройства устанавливается флаг tp->tty events, чтобы задача терминала обратила на него внимание при следующем запуске. Затем устройство удаляется из списка таймеров. Наконец, делается вызов функции interrupt, чтобы отправить задаче сообщение. Как упоминалось ранее при обсуждении задачи часов, функция tty wakeup логически является частью кода, обслуживающего прерывания, так как вызывается только оттуда. Следующая функция, settimer, устанавливает таймеры, определяющие возврат из вызова read в неканоническом режиме. Она принимает два аргумента, tp, указатель на структуру tty, и оп, целое число, имеющее значение TRUE или FAL5E. Сначала в списке структур tty, на голову которого указывает timelist, ищется запись, совпадающая с параметром tp. Если такая запись обнаруживается, она исключается из списка. Если settimer вызвана для того, чтобы сбросить таймер, дело сделано. Если вызов был направлен на установку таймера, в поле tp->tty time структуры tty устройства фиксируется текущее время плюс добавка, равная значению TIME структуры termios (время измеряется в десятых долях секунды). Новая запись добавляется в список так, чтобы сохранить его упорядоченность. Наконец, только что указанный тайм-аут сравнивается со значением в глобальной переменной tty timeout, и если новый истечет раньше, он записывается в переменную на место старого. Последней функцией в файле tty.c является tty devnop, пустая функция, которая косвенно вызывается тогда, когда устройство не требует специальных действий. Как можно было видеть, адрес этой функции по умолчанию используется для инициализации различных указателей в ttyjnit перед вызовом подпрофаммы инициализации самого устройства. 3.9.5. Реализация драйвера клавиатуры Теперь мы обратимся к аппаратно-зависимому коду, обеспечивающему работу консоли в MINIX, которая состоит из клавиатуры IBM PC и отображаемого в память экрана. Физические устройства консоли полностью различны, у стандартных настольных систем экран поддерживается при помощи контроллера (которая может быть одной из дюжины типов), а работу клавиатуры обеспечивают схемы на материнской плате, взаимодействующие с однокристальным 8-разрядным компьютером в клавиатуре. Для поддержания двух различных устройств требуется два различных набора программного обеспечения, которое в MINIX находится в файлах keyboard.c и console.с. С точки зрения операционной системы, клавиатура и экран являются частями одного устройства, /dev/console. Если у видеоконтроллера достаточно памяти, то в систему может быть включена поддержка виртуальных консолей, и, помимо /dev/console, могут существовать дополнительные логические устройства, /dev/ttyd, /dev/ttyc2 и т. д. В любой момент времени на экране видна только одна из этих консолей, эта же консоль получает ввод с единственной клавиатуры. Логически клавиатура подчинена консоли, хотя подтверждается это только двумя достаточно незначительными фактами. Во-первых, в структуре консоли tty, хранящейся в tty table, есть отдельные поля для ввода и вывода, например tty devread и tty devwrite, указатели на функции в файлах keyboard.c и console.c, заполняемые при запуске. Тем не менее есть только одно поле tty priv, ссылающееся исключительно на структуры данных консоли. Во-вторых, перед входом в главный цикл tty task проводит инициализацию всех логических устройств. Для /dev/console процедура инициализации расположена в файле console.c, и код для инициализации клавиатуры вызывается из нее. С другой стороны, подразумеваемая иерархия может быть перевернута. Имея дело с устройствами ввода/вывода, мы всегда сначала рассматривали ввод, а затем вывод, и сейчас мы продолжим эту традицию, рассмотрев код keyboard.c в текущем разделе и отложив console.c до следующего. Файл keyboard.c, как и многие другие, начинается с нескольких директив #include. Но одна из них необычна. Файл keymaps/us-std.src не является заголовочным файлом С, это файл исходных кодов, который при компиляции определяет раскладку клавиатуры по умолчанию, попадающую в keyboard.o в виде инициализированного массива. Сам этот файл довольно велик, и лищь несколько типичных записей из него приведены в табл. 3.13. После директив #include следуют макросы, задающие разнообразные константы. Первая фуппа привлекается для низкоуровневого взаимодействия с контроллером клавиатуры. Большая часть из них задают адреса портов ввода/вывода или битовые комбинации, используемые при таком взаимодействии. Следующая группа макросов описывает символические имена для специальных клавиш. Макрос kb addr всегда возвращает указатель на первый элемент в массиве kb lines, так как аппаратное обеспечение IBM поддерживает только одну клавиатуру. Константа KB IN BYTES, имеющая значение 32, определяет размер клавиатурного буфера. Следующие И переменных хранят различные параметры, необходимые для правильной
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |