Главная страница  Межпроцессное взаимодействие (состязание) 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 [ 94 ] 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

флаг tty escaped устанавливается в 1, чтобы обозначить, что любое особое значение следующего полученного символа необходимо игнорировать.

Следующая часть структуры хранит информацию о текущих операциях DEV READ, DEV WRITE и DEV IOCTL. Каждая из этих операций затрагивает два процесса. Обслуживающий системный вызов сервер (обычно это файловая система) идентифицируется полем ttyjncaller. Сервер вызывает задачу терминала от лица другого процесса, заинтересованного в операции ввода/вывода, и этот процесс идентифицируется полем ttyjnproc. Как показано на рис. 3.24, при выполнении read символы копируются напрямую в приемный буфер в адресном пространстве сделавшего вызов процесса. Положение буфера задается полями ttyjnproc и ttyjn vir. Две следующие переменные, ttyjnleft и ttyjncum, фиксируют, сколько еще нужно получить символов и сколько уже получено. Аналогичный набор переменных предусмотрен и для системного вызова write. При выполнении вызова ioctl может потребоваться немедленная передача данных между запрашивающим процессом и задачей, для чего нужен виртуальный адрес, но информацию о состоянии этой операции сохранять не надо. Наконец, структура tty содержит еще несколько переменных, которые не попадают ни в какую другую категорию. Это указатели на функции, выполняющие операции DEVJOCTL и DEV CLOSE на уровне устройства, структура termios, описанная в POSIX, и структура winsize, обеспечивающая поддержку экранов с оконным интерфейсом. В последней части структуры зарезервировано место для самой входной очереди в массиве ttyjnbuf. Обратите внимание, что это массив значений типа ul6J, а не 8-битных символов char. Хотя приложения и устройства используют для представления символов 8-битные коды, язык С ужесточает требования, считая, что функция getchar должна возвращать данные более длинного типа, чтобы, помимо 256 допустимых значений байта, она могла возвращать код EOF.

Массив ttyjable, состоящий из структур tty, объявлен как EXTERN. Для каждого терминала в этом массиве резервируется по одному элементу, а количество элементов определяется константами NR C0NS, NR RS LINES и NR PTYS, заданными в файле include/minix/config.h. В обсуждаемую в книге конфигурацию включены две консоли, но MINIX можно перекомпилировать и включить поддержку до двух последовательных интерфейсов и до 64 псевдотерминалов.

В файле tty.h есть еще одно EXTERN-определение. Переменная ttyjimelist представляет собой указатель, который используется таймером для поддержания списка полей ttyjime. Файл tty.h включается во многие другие файлы, и область памяти для ttyjable и ttyjimelist резервируется при компиляции, так же как для EXTERN-переменных, объявленных в файле glo.h.

В конце файла находится текст двух макросов, buflen и bufend. Эти макроопределения многократно используются в коде задачи терминала, делающей много операций копирования в буферы и из них.

Независимый от устройств драйвер терминала

Основные функции задачи терминала и аппаратно-независимые вспомогательные функции все сконцентрированы в tty.c. Так как задача поддерживает большое число различных устройств, локализацию устройства для конкретного



вызова требуется проводить по младшему номеру устройства. Эти номера определяются макросами в начале файла. Далее следуют еще несколько макроопределений. Если устройство не инициализировано, указатели на специфические для данного устройства функции содержат нулевые значения, которые записываются компилятором. Тем самым можно создать макрос tty acbve, который возвращает FALSE в случае, когда обнаруживается нулевой указатель. Естественно, код инициализации устройства нельзя вызвать косвенно, собственно, одна из задач такого кода - это инициализация указателей, которые своим присутствием делают возможными косвенные вызовы. Далее по файлу в директивах условной компиляции следуют макроопределения, заменяющие адреса функций инициализации RS-232 или псевдотерминала нулевыми адресами, если соответствующие устройства не включены в конфигурацию. Это позволяет полностью пропустить код для означенных устройств, когда в действительности он не нужен.

Так как терминал настраивается множеством параметров, а в сетевой системе терминалов несколько, декларируется и заполняется значениями по умолчанию структура termios defaults. Сами значения задаются в файле /include/termios.h. Эта структура копируется в запись tty table при инициализации или повторной инициализации терминала. Значения по умолчанию для специальных символов показаны в табл. 3.9. В табл. 3.14 представлены значения по умолчанию для различных флагов. Далее в коде следуют строки, где аналогичным образом объявляется структура winsize defaults. Ее поля инициализируются нулями компилятором С, что подразумевает: размер окна неизвестен, используйте /etc/termcap .

Таблица 3.14. Значения по умолчанию для флагов структуры termios

Поле Значение по умолчанию

cjflag BRKINT ICRNL IXON IXANY

c oflaQ OPOST ONLCR

c cflaQ CREAD CS8 HUPCL

c lflaQ ISIG lEXTERN ICANON ECHO ECHOE

Точкой входа для задачи терминала является функция tty task. Перед тем как начать свой главный цикл, она вызывает для каждого имеющегося в конфигурации терминала функцию инициализации ttyjnit (это делается в цикле в начале функции), а затем отображается сообщение о запуске MINIX. В коде для вывода сообщения используется функция printf, но при компиляции благодаря макроопределению вызовы printf подменяются на вызовы printk. Функция printk использует для вывода функцию putk, входящую в драйвер консоли, поэтому файловая система при выполнении вывода не затрагивается. Это сообщение отправляется напрямую на экран и не может быть перенаправлено.

Главный цикл задачи аналогичен главному циклу любой другой задачи. Принимается сообщение, при помощи оператора switch для каждого типа сообщения вызывается соответствующая функция обработки, после чего генерируется ответное сообщение. Тем не менее есть и некоторые отличия. Прежде всего, много работы делается низкоуровневыми процедурами отработки прерываний, особенно при обработке ввода с терминала. В предыдущем разделе мы видели, что от-



дельные символы, полученные с клавиатуры, принимаются и накапливаются в буфере без отправки сообщения задаче каждый раз, когда поступает символ. Поэтому, прежде чем пытаться получить сообщение, драйвер терминала пробегает по всей таблице tty table, проверяя флаги tp->tty events. Тогда, если необходимо позаботиться о незаверщенных делах, вызывается функция handle events. И только когда не было найдено ни одного требующего внимания терминала, делается вызов receive. Если сообщение пришло от аппаратного обеспечения, оператор continue накоротко замыкает цикл и повторяется проверка произошедших событий.

Далее, эта задача обслуживает несколько устройств. Если получено сообщение от аппаратуры, то требующее обслуживания устройство или устройства определяются путем проверки флагов tp->tty events. Если прерывание не аппаратное, то, чтобы определить, какое устройство должно ответить на сообщение, используется поле сообщения TTY LINE. Младший номер устройства декодируется при помощи серии сравнений, и в tp помещается адрес корректной записи в таблице терминалов ttyjable. Если устройство является псевдотерминалом, вызывается do pty (из файла pty.c), после чего главный цикл перезапускается. В этом случае ответное сообщение генерируется функцией do pty. Конечно, если в конфигурации нет псевдотерминалов, данный вызов при помощи макроса заменяется пустым, описанным ранее. Можно было бы понадеяться на отсутствие попыток обращения к несуществующим устройствам, но всегда проще добавить еще одну упреждающую проверку, чем при ошибке разбираться со всей остальной системой. В ситуации, когда устройство не существует или не сконфигурировано, генерируется ответное сообщение с кодом ENXIO и управление опять передается в начало цикла.

Оставшуюся часть кода составляет то, что мы уже видели в главной функции других задач, оператор switch, проверяющий значение типа сообщения. Для запроса каждого типа вызывается соответствующая функция, do read, do write и т. д. В каждом из случаев функции сами создают ответное сообщение, вместо того чтобы возвращать необходимую информацию обратно в главный цикл. Ответное сообщение генерируется в конце цикла только при условии, что было получено сообщение неправильного типа. В этом случае оно содержит код ошибки EINVAL. Так как отправлять сообщения требуется во многих местах кода, реализована общая подпрограмма tty reply, которая отвечает за детали создания ответного послания.

Если полученное задачей tty task сообщение имеет корректный тип, а также не является результатом прерывания и не пришло от псевдотерминала, оператор switch в конце главного цикла перенаправляет его одной из функций do read, do write, dojoctl, do open, do close или do cancel. У этих функций есть два аргумента: tp, указатель на структуру tty, и адрес сообщения. Прежде чем рассматривать их по отдельности, мы упомянем некоторые общие положения. Так как задача терминала обслуживает несколько устройств, эти функции должны срабатывать быстро, чтобы главный цикл мог продолжить работу. Тем не менее do read, do write и dojoctl не всегда Morjrr сразу обеспечить выполнение всех запрошенных действий за раз. Чтобы позволить файловой системе обслуживать



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 [ 94 ] 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

© 2000 - 2018 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.