Архитектура операционной системы UNIX - Морис Бах
Шрифт:
Интервал:
Закладка:
Когда mpx получает уведомление о том, что пользователю нужно создать новое окно, он создает процесс, управляющий новым окном, и поддерживает связь с ним через псевдотерминал. Псевдотерминал — это программное устройство, работающее по принципу пары: выходные данные, направляемые к одной составляющей пары, посылаются на вход другой составляющей; входные данные посылаются тому модулю потока, который расположен выше по течению. Для того, чтобы открыть окно (Рисунок 10.24), mpx назначает псевдотерминальную пару и открывает одну из составляющих пары, направляя поток к ней (открытие драйвера служит гарантией того, что псевдотерминальная пара не была выбрана раньше). Mpx ветвится и новый процесс открывает другую составляющую псевдотерминальной пары. Mpx выдвигает модуль управления сообщениями в псевдотерминальный поток, чтобы преобразовывать управляющие сообщения в информационные (об этом в следующем параграфе), а порожденный процесс помещает в псевдотерминальный поток модуль строкового интерфейса перед запуском shell'а. Этот shell теперь выполняется на виртуальном терминале; для пользователя виртуальный терминал неотличим от физического.
/* предположим, что дескрипторы файлов 0 и 1 уже относятся к физическому терминалу */
for (;;) { /* цикл */
выбрать(ввод);
/* ждать ввода из какой-либо линии */
прочитать данные, введенные из линии;
переключить(линию с вводимыми данными) {
если выбран физический терминал: /* данные вводятся по линии физического терминала */
if (считана управляющая команда) { /* например, создание нового окна */
открыть свободный псевдотерминал;
пойти по ветви нового процесса:
if (процесс родительский) {
выдвинуть интерфейс сообщений в сторону mpx;
continue; /* возврат в цикл "для" */
}
/* процесс-потомок */
закрыть ненужные дескрипторы файлов;
открыть другой псевдотерминал из пары, выбрать stdin, stdout, stderr;
выдвинуть строковый интерфейс терминала;
запустить shell; /* подобно виртуальному терминалу */
}
/* "обычные" данные, появившиеся через виртуальный терминал */
демультиплексировать считывание данных с физического терминала, снять заголовки и вести запись на соответствующий псевдотерминал;
continue; /* возврат в цикл "для" */
если выбран логический терминал: /* виртуальный терминал связан с окном */
закодировать заголовок, указывающий назначение информации окна;
переписать заголовок и информацию на физический терминал;
continue; /* возврат в цикл "для" */
}
}
Рисунок 10.24. Псевдопрограмма мультиплексирования окон
Процесс mpx является мультиплексором, направляющим вывод данных с виртуальных терминалов на физический терминал и демультиплексирующим ввод данных с физического терминала на подходящий виртуальный. Mpx ждет поступления данных по любой из линий, используя системную функцию select. Когда данные поступают от физического терминала, mpx решает вопрос, являются ли поступившие данные управляющим сообщением, извещающим о необходимости создания нового окна или удаления старого, или же это информационное сообщение, которое необходимо разослать процессам, считывающим информацию с виртуального терминала. В последнем случае данные имеют заголовок, идентифицирующий тот виртуальный терминал, к которому они относятся; mpx стирает заголовок с сообщения и переписывает данные в соответствующий псевдотерминальный поток. Драйвер псевдотерминала отправляет данные через строковый интерфейс терминала процессам, осуществляющим чтение. Обратная процедура имеет место, когда процесс ведет запись на виртуальный терминал; mpx присоединяет заголовок к данным, информируя физический терминал, для вывода в какое из окон предназначены эти данные.
Если процесс вызывает функцию ioctl с виртуального терминала, строковый интерфейс терминала задает необходимые установки терминала для его виртуальной линии; для каждого из виртуальных терминалов установки могут быть различными. Однако, на физический терминал должна быть послана и кое-какая информация, зависящая от типа устройства. Модуль управления сообщениями преобразует управляющие сообщения, генерируемые функцией ioctl, в информационные сообщения, предназначенные для чтения и записи их процессом mpx, и эти сообщения передаются на физическое устройство.
10.4.2 Анализ потоков
Ричи упоминает о том, что им была предпринята попытка создания потоков только с процедурами "вывода" или только с процедурами обслуживания. Однако, процедура обслуживания необходима для управления потоками данных, так как модули должны иногда ставить данные в очередь, если соседние модули на время закрыты для приема данных. Процедура "вывода" так же необходима, поскольку данные должны иногда доставляться в соседние модули незамедлительно. Например, строковому интерфейсу терминала нужно вести эхо-сопровождение ввода данных на терминале в темпе с процессом. Системная функция write могла бы запускать процедуру "вывода" для следующей очереди непосредственно, та, в свою очередь, вызывала бы процедуру "вывода" для следующей очереди и так далее, не нуждаясь в механизме диспетчеризации. Процесс приостановился бы в случае переполнения очередей для вывода. Однако, со стороны ввода модули не могут приостанавливаться, поскольку их выполнение вызывается программой обработки прерываний, иначе был бы приостановлен совершенно безобидный процесс. Связь между модулями не должна быть симметричной в направлениях ввода и вывода, хотя это и делает схему менее изящной.
Также было бы желательно реализовать каждый модуль в виде отдельного процесса, но использование большого количества модулей привело бы к переполнению таблицы процессов. Модули наделяются специальным механизмом диспетчеризации — программным прерыванием, независимым от обычного планировщика процессов. По этой причине модули не могут приостанавливать свое выполнение, так как они приостанавливали бы тем самым произвольный процесс (тот, который прерван). Модули должны хранить внутри себя информацию о своем состоянии, что делает лежащие в их основе программы более громоздкими, чем если бы приостановка выполнения была разрешена.
В реализации потоков можно выделить несколько отклонений или несоответствий:
• Учет ресурсов процесса в потоках затрудняется, поскольку модулям необязательно выполняться в контексте процесса, использующего поток. Ошибочно предполагать, что все процессы одинаково используют модули потоков, поскольку одним процессам может потребоваться использование сложных сетевых протоколов, тогда как другие могут использовать простые строковые интерфейсы.
• Пользователи имеют возможность переводить терминальный драйвер в режим без обработки, в котором функция read возвращает управление через короткий промежуток времени в случае отсутствия данных (например, если newtty.c_cc[VMIN] = 0 на Рисунке 10.17). Эту особенность сложно реализовать в потоковой среде без подключения специальной программы на уровне заголовка потока.
• Потоки выступают средствами линейной связи и не могут позволить производить с легкостью мультиплексирование на уровне ядра. В примере использования окон, рассмотренном в предыдущем разделе, выполнялось мультиплексирование на уровне пользовательского процесса.
Несмотря на эти несоответствия, с потоками связываются большие надежды в совершенствовании разработки модулей драйвера.
10.5 ВЫВОДЫ
Данная глава представляет собой обзор драйверов устройств в системе UNIX. Устройства могут быть либо блочного, либо символьного типа; интерфейс между устройствами и остальной частью ядра определяется типом устройств. Интерфейсом для устройств блочного типа выступает таблица ключей устройств ввода-вывода блоками, состоящая из точек входа, соответствующих процедурам открытия и закрытия устройств и стратегической процедуре. Стратегическая процедура управляет передачей данных от и к устройству блочного типа. Интерфейсом для устройств символьного типа выступает таблица ключей устройств посимвольного ввода-вывода, которая состоит из точек входа, соответствующих процедурам открытия и закрытия устройства, чтения, записи и процедуре ioctl. Системная функция ioctl использует при обращении к устройствам символьного типа свой собственный интерфейс, который позволяет осуществлять передачу управляющей информации между процессами и устройствами. По получении прерывания от устройства ядро вызывает программу обработки соответствующего прерывания, опираясь на информацию, хранящуюся в таблице векторов прерываний, и на параметры, сообщенные устройством, от которого поступило прерывание.