Архитектура операционной системы UNIX (ЛП) - Бах Морис Дж.
Шрифт:
Интервал:
Закладка:
Рисунок 10.22. Продвижение модуля к потоку
Процессы могут "продвигать" модули к открытому потоку, используя вызов системной функции ioctl. Ядро помещает выдвинутый модуль сразу под заголовком потока и связывает указатели очереди таким образом, чтобы сохранить двунаправленную структуру списка. Модули, расположенные в потоке ниже, не беспокоятся о том, связаны ли они с заголовком потока или же с выдвинутым модулем: интерфейсом выступает процедура "вывода" следующей очереди в потоке; а следующая очередь принадлежит только что выдвинутому модулю. Например, процесс может выдвинуть модуль строкового интерфейса в поток терминального драйвера с целью обработки символов стирания и удаления (Рисунок 10.22); модуль строкового интерфейса не имеет тех же составляющих, что и строковые интерфейсы, рассмотренные в разделе 10.3, но выполняет те же функции. Без модуля строкового интерфейса терминальный драйвер не обработает вводные символы и они поступят в заголовок потока в неизмененном виде. Сегмент программы, открывающий терминал и выдвигающий строковый интерфейс, может выглядеть следующим образом:
fd = open("/dev/ttyxy", O_RDWR);
ioctl(fd, PUSH, TTYLD);
где PUSH — имя команды, а TTYLD — число, идентифицирующее модуль строкового интерфейса. Не существует ограничения на количество модулей, могущих быть выдвинутыми в поток. Процесс может выталкивать модули из потока в порядке поступления, "первым пришел — первым вышел", используя еще один вызов системной функции ioctl
ioctl(fd, POP, 0);
При том, что модуль строкового интерфейса выполняет обычные функции по управлению терминалом, соответствующее ему устройство может быть средством сетевой связи вместо того, чтобы обеспечивать связь с одним-единственным терминалом. Модуль строкового интерфейса работает одинаково, независимо от того, какого типа модуль расположен ниже него. Этот пример наглядно демонстрирует повышение гибкости вследствие соединения модулей ядра.
10.4.1 Более детальное рассмотрение потоков
Пайк описывает реализацию мультиплексных виртуальных терминалов, использующую потоки (см. [Pike 84]). Пользователь видит несколько виртуальных терминалов, каждый из которых занимает отдельное окно на экране физического терминала. Хотя в статье Пайка рассматривается схема для интеллектуальных графических терминалов, она работала бы и для терминалов ввода-вывода тоже; каждое окно занимало бы целый экран и пользователь для переключения виртуальных окон набирал бы последовательность управляющих клавиш.
Рисунок 10.23. Отображение виртуальных окон на экране физического терминала
На Рисунке 10.23 показана схема расположения процессов и модулей ядра. Пользователь вызывает процесс mpx, контролирующий работу физического терминала. Mpx читает данные из линии физического терминала и ждет объявления об управляющих событиях, таких как создание нового окна, переключение управления на другое окно, удаление окна и т. п.
Когда 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 присоединяет заголовок к данным, информируя физический терминал, для вывода в какое из окон предназначены эти данные.