Главная страница Межпроцессное взаимодействие (состязание) другие вызовы, от функций требуется немедленный ответ. Поэтому, если запрос не может быть удовлетворен немедленно, в ответном сообщении в поле состояния доставляется код SUSPEND. На рис. 3.24 это соответствует сообщению, отмеченному (3), и приводит к приостановке процесса-инициатора вызова в то время, как файловая система выходит из блокировки. Сообщения под номерами (7) и (8) будут отправлены позже, после заверщения операции. Если операция может быть выполнена полностью или произошла ошибка, то в поле состояния ответного сообщения возвращается либо количество переданных байтов, либо код ошибки. В последнем случае файловая система немедленно отправит сделавшему вызов процессу ответное сообщение, пробуждая его. Чтение с терминала фундаментально отличается от чтения с диска. Драйвер диска отдает команды аппаратному обеспечению, и в итоге тоже возвращаются данные, если не произошел механический или электрический сбой. В случае терминала компьютер может высветить на экране приглашение, но не существует способа принудить сидящего за клавиатурой человека начать печатать. Хотя бы потому, что нет никакой гарантии, что за клавиатурой кто-то сидит. Чтобы обеспечить быстрый возврат, функция do read начинает с того, что сохраняет информацию о запросе. Благодаря этому он может выполнен позже, когда прибудет недостающая информация. Сначала должен быть проведен контроль ошибок. Ошибка возникает в случае, если устройство все еще ожидает ввод, чтобы выполнить предыдущий запрос, или если переданные в сообщении параметры неправильны. После того как проверка пройдена, информация готова к записи в поля записи tp->tty table, соответствующей устройству. Следующим шагом в переменную tp->inleft записывается количество полученных символов. Это важный шаг, так как ги роменная tp->inleft необходима для определения момента завершения чтения. В каноническом режиме значение tp->inleft декрементируется с каждым возвращенным символом до тех пор, пока не будет получен символ новой строки, после чего эта переменная сразу обнуляется. В неканоническом режиме она изменяется по-другому, но так или иначе, когда запрос выполнен, по тайм-ауту или после получения минимального необходимого количества символов, в эту переменную записывается нуль. Когда значение tp->tty inleft достигает нуля, отправляется ответное сообщение. Как мы увидим, ответное сообщение может генерироваться в нескольких местах кода. Кроме того, иногда необходимо удостовериться, что выполняющий чтение процесс ожидает ответа. Здесь индикатором служит ненулевое значение tp->ttyjnleft. В каноническом режиме терминал ожидает ввода до тех пор, пока не будет введено запрошенное количество символов или не будет получен символ конца строки или конца файла. Чтобы проверить, находится ли терминал в каноническом режиме, тестируется значение бита ICANON структуры termios. Если бит не установлен, проверяется значение переменных MIN и TIME, с целью определиться в дальнейших действиях. В табл. 3.10 мы видели, как различные комбинации параметров MIN и TIME определяют поведение при чтении с терминала. Сначала проверяется значение параметра TIME. Нулевое значение соответствует левому столбцу таблицы, и в этом случае других проверок в текущий момент не нужно. Если значение TIME не равно нулю, проверяется параметр MIN. Если это нуль, де- лается вызов settimer для запуска таймера, который по истечении заданного интервала времени прервет запрос DEV READ, даже если не будет прочитано ни одного байта. В переменную tp->tty min в этом случае записывается 1, чтобы вызов завершился немедленно после того, как до истечения тайм-аута будет получен хотя бы один символ. В этой точке не проверяется, имеются ли уже введенные символы, поэтому может оказаться, что запроса уже ожидают несколько символов. Как только обнаруживаются уже введенные символы, они возвращаются процессу, но не больше, чем задано при вызове read. В том случае, если и MIN и TIME не равны нулю, таймер ведет себя по-другому - отсчитывает время между символами. Он запускается только после того, как введен первый символ, и перезапускается с каждым новым. В переменной tp->tty eotct в неканоническом режиме подсчитываются введенные символы. Ее значение проверяется, и при равенстве нулю (ни одного символа еше не получено) таймер останавливается. Оба вызова settimer обрамлены вызовами lock и unlock, чтобы предотвратить прерывания от часов в то время, когда работает settimer. В любом случае после этого блока условных операторов вызывается функция in transfer, чтобы напрямую передать уже имеющиеся во входной очереди символы на чтение процессу. Затем следует вызов handle events, который, в свою очередь, может поместить новые данные во входную очередь и который снова вызывает in transfer. Такое кажущееся дублирование вызовов требует дополнительных пояснений. Хотя до сих пор обсуждение велось в терминах клавиатурного ввода, функция do read не зависит от конкретных устройств и обслуживает в том числе удаленные терминалы, подключаемые по последовательной линии. Возможна ситуация, когда предыдущий поток ввода заполнил буфер, и ввод был приостановлен. Первый вызов in transfer не запускает поток, но вызов handle events может это сделать. То, что он при этом еще раз вызывает in transfer, это всего лишь небольшой довесок. Нам важно, что удаленный терминал снова получит разрешение отправлять данные. Оба вызова способны привести к тому, что запрос будет выполнен и файловой системе будет отправлено ответное сообщение. В качестве флага-индикатора отправки ответного сообщения используется переменная tp->tty inleft. Если ее значение не равно нулю, do read самостоятельно генерирует и отправляет ответ. За это отвечают последние девять строк кода функции. Если в исходном запросе было указано неблокирующее чтение, файловой системе дается указание вернуть код ошибки EAGAIN сделавшему вызов процессу. Если в запросе было затребовано обыкновенное блокирующее чтение, файловая система получает код SUSPEND, который разблокирует ее, но дает указание оставить процесс-инициатор вызова заблокированным. В данном случае в поле tp->ttyJnrepcode записывается значение REVIVE. Когда позднее вызов read будет выполнен (если вообще будет выполнен), этот код помещается в ответное сообщение файловой системе, с целью информировать ее о том, что запросивший чтение процесс был приостановлен и должен быть разбужен. Функция do write подобна da read, но проще, так как при обработке системного вызова read нужно учитывать меньшее количество разнообразных настроек. Сначала делается ряд проверок, подобным проверкам в do read, из которых мы узнаем, что предыдущий запрос уже обработан и переданные параметры кор- ректны. Затем параметры сообщения копируются в структуру tty. После этого вызывается функция handle events и проверяется значение переменной tp-> tty outleft, чтобы узнать, заверщена ли работа. Если да, значит, ответное сообщение уже было отправлено handle events и делать больше нечего. Если нет, генерируется ответ, параметры которого зависят от того, был ли исходный вызов write блокирующим или нет.
Далее следует довольно большая, но не сложная для понимания функция, dojoctl. Тело этой функции состоит из двух операторов switch. Первый определяет размер параметра, на который ссылается указатель в сообщении. Если размер параметра не равен нулю, проверяется его корректность. В этом месте нельзя контролировать содержимое, но можно удостовериться, что структура имеет требуемый размер, начинается по указанному адресу и умещается в том сегменте, где она должна находиться. Остаток функции составляет еще один оператор switch, проверяющий тип запрошенной операции ioctL К несчастью, поддержка требуемых стандартом POSIX операций подразумевает, что для операций ioctl необходимо изобретать имена, которые напоминают, но не дублируют POSIX-имена. Соответствие между именами запросов POSIX и параметрами вызова ioctl в MINIX показано в табл. 3.15. Операция TCGETS обслуживает пользовательский вызов tcgetattr и просто возвращает копию структуры tp->tty termios для терминального устройства. Следующие четыре типа запросов разделяют общий код. Типы запросов TGETSW, TCSETSF и TCSETS соответствуют вызовам функции POSIX tcsetattr, и основное действие в этом случае общее: структура termios копируется в структуру терминала tty. Для вызова TCSETS копирование происходит немедленно, а для TCSETSW и TCSETSF выполняется после того, как завершен вывод. Копирование производится вызовом phys copy, следующим за вызовом setattr. Если вызов tcsetattr был сделан с модификатором, требующим отложить выполнение до момента, пока не будет завершен текущий вывод, то, если проверка перемен-
|
© 2000 - 2025 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |