Скачиваний:
21
Добавлен:
09.05.2014
Размер:
354.82 Кб
Скачать

10.4 Потоки

Схема реализации драйверов устройств, хотя и отвечает заложенным требо-

ваниям, страдает некоторыми недостатками, которые с годами стали заметнее.

Разные драйверы имеют тенденцию дублировать свои функции, в частности драй-

веры, которые реализуют сетевые протоколы и которые обычно включают в себя

секцию управления устройством и секцию протокола. Несмотря на то, что секция

протокола должна быть общей для всех сетевых устройств, на практике это не

так, поскольку ядро не имеет адекватных механизмов для общего использования.

Например, символьные списки могли бы быть полезными благодаря своим возмож-

ностям в буферизации, но они требуют больших затрат ресурсов на посимвольную

обработку. Попытки обойти этот механизм, чтобы повысить производительность

системы, привели к нарушению модульности подсистемы управления вводом-выво-

дом. Отсутствие общности на уровне драйверов распространяется вплоть до

320

уровня команд пользователя, на котором несколько команд могут выполнять об-

щие логические функции, но различными средствами. Еще один недостаток пост-

роения драйверов заключается в том, что сетевые протоколы требуют использо-

вания средства, подобного строковому интерфейсу, в котором каждая дисциплина

реализует одну из частей протокола и составные части соединяются гибким об-

разом. Однако, соединить традиционные строковые интерфейсы довольно трудно.

Ричи недавно разработал схему, получившую название "потоки" (streams),

для повышения модульности и гибкости подсистемы управления вводом-выводом.

Нижеследующее описание основывается на его работе [Ritchie 84b], хотя реали-

зация этой схемы в версии V слегка отличается. Поток представляет собой пол-

нодуплексную связь между процессом и драйвером устройства. Он состоит из со-

вокупности линейно связанных между собой пар очередей, каждая из которых

(пара) включает одну очередь для ввода и другую - для вывода. Когда процесс

записывает данные в поток, ядро посылает данные в очереди для вывода; когда

драйвер устройства получает входные данные, он пересылает их в очереди для

ввода к процессу, производящему чтение. Очереди обмениваются сообщениями с

соседними очередями, используя четко определенный интерфейс. Каждая пара

очередей связана с одним из модулей ядра, таким как драйвер, строковый ин-

терфейс или протокол, и модули ядра работают с данными, прошедшими через со-

ответствующие очереди.

Каждая очередь представляет собой структуру данных, состоящую из следую-

щих элементов:

* процедуры открытия, вызываемой во время выполнения системной функции

open

* процедуры закрытия, вызываемой во время выполнения системной функции

close

* процедуры "вывода", вызываемой для передачи сообщения в очередь

* процедуры "обслуживания", вызываемой, когда очередь запланирована к ис-

полнению

* указателя на следующую очередь в потоке

* указателя на список сообщений, ожидающих обслуживания

* указателя на внутреннюю структуру данных, с помощью которой поддержива-

ется рабочее состояние очереди

* флагов, а также верхней и нижней отметок, используемых для управления

потоками данных, диспетчеризации и поддержания рабочего состояния очере-

ди.

Ядро выделяет пары очередей, соседствующие в памяти; следовательно, оче-

редь легко может отыскать своего партнера по паре.

+----------+

| Индекс |

+-----------------------+ файла |

| |устройства|

v +----------+

+------------+-----------+

Заголовок | Очередь | Очередь |

потока | для вывода | для ввода |

+------+-----+-----------+

| ^

v |

+------------+-----+-----+

Драйвер | Очередь | Очередь |------- пара очередей

| для вывода | для ввода |

+------------+-----------+

Рисунок 10.20. Поток после открытия

Устройство с потоковым драйвером является устройством посимвольного вво-

321

да-вывода; оно имеет в таблице ключей устройств соответствующего типа специ-

альное поле, которое указывает на структуру инициализации потока, содержащую

адреса процедур, а также верхнюю и нижнюю отметки, упомянутые выше. Когда

ядро выполняет системную функцию open и обнаруживает, что файл устройства

имеет тип "специальный символьный", оно проверяет наличие нового поля в таб-

лице ключей устройств посимвольного ввода-вывода. Если в таблице отсутствует

соответствующая точка входа, то драйвер не является потоковым, и ядро выпол-

няет процедуру, обычную для устройств посимвольного ввода-вывода. Однако,

при первом же открытии потокового драйвера ядро выделяет две пары очередей -

одну для заголовка потока и другую для драйвера. У всех открытых потоков мо-

дуль заголовка имеет идентичную структуру: он содержит общую процедуру "вы-

вода" и общую процедуру "обслуживания" и имеет интерфейс с модулями ядра бо-

лее высокого уровня, выполняющими функции read, write и ioctl. Ядро инициа-

лизирует структуру очередей драйвера, назначая значения указателям каждой

очереди и копируя адреса процедур драйвера из структуры инициализации драй-

вера, и запускает процедуру открытия. Процедура открытия драйвера выполняет

обычную инициализацию, но при этом сохраняет информацию, необходимую для

повторного обращения к ассоциированной с этой процедурой очереди. Наконец,

ядро отводит специальный указатель в копии индекса в памяти для ссылки на

заголовок потока (Рисунок 10.20). Когда еще один процесс открывает устройст-

во, ядро обнаруживает назначенный ранее поток с помощью этого указателя и

запускает процедуру открытия для всех модулей потока.

Модули поддерживают связь со своими соседями по потоку путем передачи

сообщений. Сообщение состоит из списка заголовков блоков, содержащих инфор-

мацию сообщения; каждый заголовок блока содержит ссылку на место расположе-

ния начала и конца информации блока. Существует два типа сообщений - управ-

ляющее и информационное, которые определяются указателями типа в заголовке

сообщения. Управляющие сообщения могут быть результатом выполнения системной

функции ioctl или результатом особых условий, таких как зависание терминала,

а информационные сообщения могут возникать в результате выполнения системной

функции write или в результате поступления данных от устройства.

Сообщение 1 Сообщение 2 Сообщение 3

+---------+ +---------+ +---------+

| Блок +--------->| +-------->| |

+----+----+ +---------+ +----+----+

v v

+---------+ +---------+

| | | |

+----+----+ +---------+

v

+---------+

| |

+---------+

Рисунок 10.21. Сообщения в потоках

Когда процесс производит запись в поток, ядро копирует данные из адрес-

ного пространства задачи в блоки сообщения, которые выделяются модулем заго-

ловка потока. Модуль заголовка потока запускает процедуру "вывода" для моду-

ля следующей очереди, которая обрабатывает сообщение, незамедлительно пере-

дает его в следующую очередь или ставит в эту же очередь для последующей об-

работки. В последнем случае модуль связывает заголовки блоков сообщения в

список с указателями, формируя двунаправленный список (Рисунок 10.21). Затем

он устанавливает в структуре данных очереди флаг, показывая тем самым, что

имеются данные для обработки, и планирует собственное обслуживание. Модуль

включает очередь в список очередей, требующих обслуживания и запускает меха-

низм диспетчеризации; планировщик (диспетчер) вызывает процедуры обслужива-

322

ния для каждой очереди в списке. Ядро может планировать обслуживание модулей

по программному прерыванию, подобно тому, как оно вызывает функции в таблице

ответных сигналов (см. главу 8); обработчик программных прерываний вызывает

индивидуальные процедуры обслуживания.

+----------+

| Индекс |

+-----------------------+ файла |

| |устройства|

v +----------+

+------------+-----------+

Заголовок | Очередь | Очередь |

потока | для вывода | для ввода |

+------+-----+-----------+

| ^

| |

v |

+------------+-----------+

Строковый | Очередь | Очередь |

интерфейс | для вывода | для ввода |

+------+-----+-----------+

| ^

| |

v |

+------------+-----+-----+

Терминальный | Очередь | Очередь |

драйвер | для вывода | для ввода |

+------------+-----------+

Рисунок 10.22. Продвижение модуля к потоку

Процессы могут "продвигать" модули к открытому потоку, используя вызов

системной функции ioctl. Ядро помещает выдвинутый модуль сразу под заголов-

ком потока и связывает указатели очереди таким образом, чтобы сохранить дву-

направленную структуру списка. Модули, расположенные в потоке ниже, не бес-

покоятся о том, связаны ли они с заголовком потока или же с выдвинутым моду-

лем: интерфейсом выступает процедура "вывода" следующей очереди в потоке; а

следующая очередь принадлежит только что выдвинутому модулю. Например, про-

цесс может выдвинуть модуль строкового интерфейса в поток терминального

драйвера с целью обработки символов стирания и удаления (Рисунок 10.22); мо-

дуль строкового интерфейса не имеет тех же составляющих, что и строковые ин-

терфейсы, рассмотренные в разделе 10.3, но выполняет те же функции. Без мо-

дуля строкового интерфейса терминальный драйвер не обработает вводные симво-

лы и они поступят в заголовок потока в неизмененном виде. Сегмент программы,

открывающий терминал и выдвигающий строковый интерфейс, может выглядеть сле-

дующим образом:

fd = open("/dev/ttyxy",O_RDWR);

ioctl(fd,PUSH,TTYLD);

где PUSH - имя команды, а TTYLD - число, идентифицирующее модуль строкового

интерфейса. Не существует ограничения на количество модулей, могущих быть

выдвинутыми в поток. Процесс может выталкивать модули из потока в порядке

поступления, "первым пришел - первым вышел", используя еще один вызов сис-

темной функции ioctl

ioctl(fd,POP,0);

При том, что модуль строкового интерфейса выполняет обычные функции по

323

управлению терминалом, соответствующее ему устройство может быть средством

сетевой связи вместо того, чтобы обеспечивать связь с одним-единственным

терминалом. Модуль строкового интерфейса работает одинаково, независимо от

того, какого типа модуль расположен ниже него. Этот пример наглядно демонст-

рирует повышение гибкости вследствие соединения модулей ядра.

10.4.1 Более детальное рассмотрение потоков

Пайк описывает реализацию мультиплексных виртуальных терминалов, исполь-

зующую потоки (см. [Pike 84]). Пользователь видит несколько виртуальных тер-

миналов, каждый из которых занимает отдельное окно на экране физического

терминала. Хотя в статье Пайка рассматривается схема для интеллектуальных

графических терминалов, она работала бы и для терминалов ввода-вывода тоже;

каждое окно занимало бы целый экран и пользователь для переключения вирту-

альных окон набирал бы последовательность управляющих клавиш.

+---------+ +---------+ +-----------------+

Уровень | shell 1 | | shell 2 | | mpx |

пользователя +---------+ +---------+ +-----------------+

---------------+---------------+-----------+---+-------+----

Уровень ядра | ^ | ^ +--+ ^ | ^ | ^

| | | | | +--+ | | | |

| | | | | | | | | |

v | v | со- v | v | со- | |

терми- +-+++ терми- +-+++ об-+-+++ +-+++об- | |

нальная | | | нальная | | | ще-| | | | | |ще- | |

линия +++-+ линия +++-+ ния+++-+ +++-+ния | |

| ^ +-----------+-^------+ ^ | ^ | |

| | | +---------+-+--------+ | | | |

| | | | | | +-----------+ | | |

v | v | v | v +-----------+ v |

+-+++-+++ +-+++-+++ +-+++

псевдо- | | | | | | | | | | псевдо- | | |

терми- +++-+++-+ +++-+++-+ терми- +-+-+

нальная | ^ | ^ | ^ | ^ нальная терми-

пара 1 | +-+ | | +-+ | пара 2 нальный

+-----+ +-----+ драйвер

Рисунок 10.23. Отображение виртуальных окон на экране физи-

ческого терминала

На Рисунке 10.23 показана схема расположения процессов и модулей ядра.

Пользователь вызывает процесс mpx, контролирующий работу физического терми-

нала. Mpx читает данные из линии физического терминала и ждет объявления об

управляющих событиях, таких как создание нового окна, переключение управле-

ния на другое окно, удаление окна и т.п.

Когда mpx получает уведомление о том, что пользователю нужно создать но-

вое окно, он создает процесс, управляющий новым окном, и поддерживает связь

с ним через псевдотерминал. Псевдотерминал - это программное устройство, ра-

ботающее по принципу пары: выходные данные, направляемые к одной составляю-

щей пары, посылаются на вход другой составляющей; входные данные посылаются

тому модулю потока, который расположен выше по течению. Для того, чтобы отк-

рыть окно (Рисунок 10.24), mpx назначает псевдотерминальную пару и открывает

одну из составляющих пары, направляя поток к ней (открытие драйвера служит

гарантией того, что псевдотерминальная пара не была выбрана раньше). Mpx

ветвится и новый процесс открывает другую составляющую псевдотерминальной

324

+----------------------------------------------------------------+

| /* предположим, что дескрипторы файлов 0 и 1 уже относятся к |

| физическому терминалу */ |

| для(;;) /* цикл */ |

| { |

| выбрать(ввод); /* ждать ввода из какой-либо линии */ |

| прочитать данные, введенные из линии; |

| переключить(линию с вводимыми данными) |

| { |

| если выбран физический терминал: /* данные вводятся по ли- |

| нии физического терми- |

| нала */ |

| если(считана управляющая команда) /* например, создание |

| нового окна */ |

| { |

| открыть свободный псевдотерминал; |

| пойти по ветви нового процесса: |

| если(процесс родительский) |

| { |

| выдвинуть интерфейс сообщений в сторону mpx; |

| продолжить; /* возврат в цикл "для" */ |

| } |

| /* процесс-потомок */ |

| закрыть ненужные дескрипторы файлов; |

| открыть другой псевдотерминал из пары, выбрать stdin, |

| stdout, stderr; |

| выдвинуть строковый интерфейс терминала; |

| запустить shell; /* подобно виртуальному терминалу */|

| } |

| /* "обычные" данные, появившиеся через виртуальный терминал */ |

| демультиплексировать считывание данных с физического тер-|

| минала, снять заголовки и вести запись на соответствую- |

| щий псевдотерминал; |

| продолжить; /* возврат в цикл "для" */ |

| |

| если выбран логический терминал: /* виртуальный терминал |

| связан с окном */ |

| закодировать заголовок, указывающий назначение информации|

| окна; |

| переписать заголовок и информацию на физический терминал;|

| продолжить; /* возврат в цикл "для" */ |

| } |

| } |

+----------------------------------------------------------------+

Рисунок 10.24. Псевдопрограмма мультиплексирования окон

пары. Mpx выдвигает модуль управления сообщениями в псевдотерминальный по-

ток, чтобы преобразовывать управляющие сообщения в информационные (об этом в

следующем параграфе), а порожденный процесс помещает в псевдотерминальный

поток модуль строкового интерфейса перед запуском shell'а. Этот shell теперь

выполняется на виртуальном терминале; для пользователя виртуальный терминал

неотличим от физического.

Процесс mpx является мультиплексором, направляющим вывод данных с вирту-

альных терминалов на физический терминал и демультиплексирующим ввод данных

с физического терминала на подходящий виртуальный. Mpx ждет поступления дан-

ных по любой из линий, используя системную функцию select. Когда данные пос-

тупают от физического терминала, mpx решает вопрос, являются ли поступившие

325

данные управляющим сообщением, извещающим о необходимости создания нового

окна или удаления старого, или же это информационное сообщение, которое не-

обходимо разослать процессам, считывающим информацию с виртуального термина-

ла. В последнем случае данные имеют заголовок, идентифицирующий тот вирту-

альный терминал, к которому они относятся; mpx стирает заголовок с сообщения

и переписывает данные в соответствующий псевдотерминальный поток. Драйвер

псевдотерминала отправляет данные через строковый интерфейс терминала про-

цессам, осуществляющим чтение. Обратная процедура имеет место, когда процесс

ведет запись на виртуальный терминал; mpx присоединяет заголовок к данным,

информируя физический терминал, для вывода в какое из окон предназначены эти

данные.

Если процесс вызывает функцию ioctl с виртуального терминала, строковый

интерфейс терминала задает необходимые установки терминала для его виртуаль-

ной линии; для каждого из виртуальных терминалов установки могут быть раз-

личными. Однако, на физический терминал должна быть послана и кое-какая ин-

формация, зависящая от типа устройства. Модуль управления сообщениями преоб-

разует управляющие сообщения, генерируемые функцией ioctl, в информационные

сообщения, предназначенные для чтения и записи их процессом mpx, и эти сооб-

щения передаются на физическое устройство.

Соседние файлы в папке материалы к собеседованию