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

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

ной tp->tty outleft показывает, что вывод действительно еще не завершен, параметры запроса помещаются в структуру tty терминала для последующей обработки. Вызов tcdrain приостанавливает программу до тех пор, пока не будет завершен вывод. Он транслируется в вызов ioctl типа TCDRAIN. Если вывод уже завершен, дальнейшей обработки не требуется. Иначе информация также записывается в структуру tty.

Функция POSIX tcflush отменяет незавершенный ввод и/или неотправленные на вывод данные, в зависимости от переданного аргумента. Соответствующая операция ioctl состоит из вызова ttyjcancel, который обслуживает все терминалы, и/или вызова зависящей от устройства функции, адрес которой хранится в tp->tty ocancel. Вызов tcflow аналогичным образом транслируется в вызов ioctl. Чтобы приостановить или вновь разрешить вывод, в переменную tp-> ttyjnhibited записывается значение TRUE или FALSE, а затем устанавливается флаг tp->tty events. Чтобы приостановить или вновь разрешить ввод, удаленному терминалу отправляется, соответственно, код STOP (обычно CTRL+S) или START (CTRL+Q). Для этого используется специфичная для устройства подпрограмма, адрес которой хранится в tp->tty echo.

Большая часть остальных операций, обрабатываемых dojoctl, выполняются одной строчкой кода, то есть просто вызовом соответствующей функции. В случае операций KIOCSMAP (загрузка раскладки клавиатуры) и TI0CSF0N (загрузка шрифта) делается проверка того, что устройство действительно является консолью, так как эти операции неприменимы к другим типам терминалов. Если используются виртуальные терминалы, на всех консолях будет одна раскладка клавиатуры и шрифт. Аппаратное обеспечение не позволяет легко изменить это поведение. Операции, работающие с размером окна, переносят данные структуры winsize между пользовательским процессом и задачей терминала. Обратите особое внимание на комментарий под кодом для операции TI0CSWINSZ. Когда процесс меняет размер окна, от ядра ожидается, что оно отправит сигнал SIGWINCH всем процессам из той же фуппы процессов. По стандарту POSIX сигнал не требуется, но любой, кто хочет использовать эти структуры, должен подумать о том, чтобы добавить сюда код, инициирующий сигнал.

Последние два варианта из dojoctl поддерживают функции tcgetpgrp и tcsetpgrp стандарта POSIX. С этими типами не связано никаких действий, и они всегда возвращают ошибку. И здесь нет ничего неправильного. Эти функции необходимы для поддержки управления задачами, для возможности приостанавливать и перезапускать процесс с клавиатуры. Управление задачами не требуется POSIX и не поддерживается в MINIX. Тем не менее, по стандарту POSIX, эти функции должны иметься даже в отсутствие управления задачами, с целью гарантировать переносимость программ.

Основная задача функции do open проста - она увеличивает значение переменной tp->tty openct для устройства, чтобы впоследствии можно было судить, открыто оно или нет. Вместе с тем сначала нужно провести несколько проверок. Согласно стандарту POSIX, первый процесс, открывающий терминал, должен быть лидером сеанса, и, когда этот процесс завершается, у других процессов из той же группы отбирается доступ к терминалу. Демонам необходимо иметь возможность



выводить сообщения об ошибках, и если вывод демонов не перенаправлен в файл, он должен поступать на экран, который нельзя закрыть. Соответственно, в MINIX существует устройство /dev/log. Физически это то же устройство, что и /dev/ console, но оно адресуется отдельным младшим номером и обрабатывается иначе. Это устройство только для записи, поэтому do open возвращает ошибку EACCESS, если сделана попытка открыть его для чтения. Еще одна проверка, которую делает do open, это проверка флага 0 NOCTTY. Если флаг не установлен и устройство не является /dev/log, терминал становится управляющим терминалом для данной группы процессов. Для чего номер процесса, сделавшего вызов, заносится в поле tp->tty pgrp записи таблицы tty table. Затем инкрементируется переменная tp-> tty openct и отправляется ответное сообщение.

Терминальное устройство может быть открыто несколько раз, и потом функции do close нечего делать, кроме как уменьшить tp->tty openct на единицу. Следующая проверка препятствует закрытию устройства, если это /dev/log. Если обнаруживается, что устройство закрыто в последний раз, для отмены ввода вызывается tp->ttyjcancel. Также вызываются специфические для устройства функции tp->tty ocancel и tp->tty close. Различным полям структуры tty закрытого устройства присваиваются значения по умолчанию и отправляется ответное сообщение.

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

1. Когда процесс был завершен, он мог выполнять чтение.

2. Когда процесс был завершен, он мог выполнять запись.

3. Процесс мог быть приостановлен tcdrain до тех пор, пока его вывод не будет завершен.

Для каждого из этих случаев делается проверка и вызывается основная функция, tp->icancel, или специфическая для устройства функция, адрес которой хранится в поле tp->ocancel. В последнем случае единственное, что требуется, - сбросить флаг tp->ttyJoreq, тем самым обозначив, что операция ioctl завершена. В завершение устанавливается флаг tp->tty events и отправляется ответное сообщение.

Код поддержки драйвера терминала

Мы рассмотрели функции верхнего уровня, вызываемые в главном цикле tty task, а сейчас настала пора обратиться к коду, обеспечивающему их работу. Мы начнем с функции handle events. Как упоминалось ранее, при каждом проходе главного цикла задачи терминала для каждого терминального устройства проверяется флаг tp->tty events, и если терминалу требуется внимание, вызывается handle events. Подпрограммы do read и do write также вызывают handle events. Эта подпро-фамма должна выполняться быстро. Она сбрасывает флаг tp->tty events и обращается к специфическим для устройства функциям записи или чтения, опираясь на указатели tp->tty devread и tp->tty devwrite. Функции вызываются без уело-



ВИЙ, так как нет возможности проверить, что вызвало установку флага, чтение или запись. При разработке было решено, что проверять для каждого устройства два флага - более дорогостоящая операция, чем вызывать две функции в том случае, если устройство активно. Кроме того, большую часть времени отображается эхо вводимых символов, и полученный от терминала символ должен быть выведен на экран, поэтому необходимы оба вызова. Как упоминалось при обсуждении обработки tcsetattr в dojoctl, POSIX не запрещает откладывать выполнение управляющих операций до тех пор, пока не будет завершен текущий вывод, поэтому сразу после вызова tty devwrite имеет смысл позаботиться об операциях ioctl. Это делается последующими строками, где, если имеются текущие управляющие запросы, вызывается devjoctl.

Так как флаг tp->tty events устанавливается прерываниями и от быстрого устройства символы могут поступать с большой скоростью, существует вероятность, что за время выполнения функций чтения и записи, а также devjoctl, произошло другое прерывание и флаг снова возведен. Извлечению данных из буфера, куда они помещаются обработчиком прерываний, придается большой приоритет. Поэтому handle events повторяет вызов функций чтения и записи, если обнаруживается, что флаг снова установлен. Когда входной поток останавливается (это может быть и выходной поток, но для входа более характерны подобные повторяющиеся запросы), чтобы передать символы из входной очереди в буфер сделавшего вызов процесса, вызывается in transfer. Эта функция сама отправляет ответное сообщение, если переданные символы завершают запрос (в каноническом режиме такое может произойти в том случае, когда получено запрошенное количество символов, либо когда получен символ конца строки, либо когда достигнут конец файла). Если это произошло, переменная tp->tty left после возврата в handle events будет равна нулю. Соответствующий контроль выполняется, и, если количество переданных символов достигло необходимого минимума, отправляется ответное сообщение.

Далее перейдем к функции in transfer, которая отвечает за передачу данных из входной очереди в адресном пространстве задачи в буфер пользовательского процесса, запросившего ввод. Напрямую копировать блок данных в этом случае невозможно. Входная очередь представляет собой круговой буфер, символы в которой необходимо проверять на признак конца файла или, если действует канонический режим, проверять, что ввод продолжается с новой строки. Кроме того, символы во входной очереди 16-битные, а буфер принимающего процесса является массивом 8-битных символов. Поэтому используется промежуточный локальный буфер. Символы один за одним проверяются и помещаются в буфер, и когда тот заполняется, вызывается функция phys capy, которая переносит содержимое промежуточного буфера в приемный буфер пользовательского процесса.

Для того чтобы определить, есть ли работа для in transfer, задействуются три переменные: tp->ttyjnleft, tp->tty eotct и tp->tty min. Первые две управляют главным циклом процедуры. Ранее упоминалось, что в tp->ttyjnleft изначально помещается количество символов, запрошенных в вызове read. Обычно эта переменная уменьшается на единицу с каждым новым полученным символом, но она



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.
Копирование материалов разрешено исключительно при условии цититирования.