Драйвер терминала поддерживает два режима ввода. В каноническом режиме ввод группируется в строки (с одним из символов-разделителей в конце), позволяя редактировать текущую строку. Неканонический режим дает возможность приложению считывать ввод посимвольно, не дожидаясь ввода символа-разделителя; редактирование текущей строки при этом отключено. Завершение ввода в неканоническом режиме определяется полями MIN и TIME структуры termios, обозначающими минимальное количество символов, которое нужно прочитать, и, соответственно, время ожидания, относящееся к операции чтения. Мы описали четыре разных сценария чтения в неканоническом режиме.

Так сложилось, что драйверы терминала в седьмой редакции UNIX и в системе BSD предоставляли три режима ввода: с обработкой, без обработки и cbreak. Каждый из них обеспечивал определенную степень интерпретации ввода и вывода. Cbreak и режим без обработки можно эмулировать путем изменения различных полей в структуре termios.

Для выполнения других операций с терминалом предусмотрен ряд функций. Это касается изменения скорости терминала и управления строками (генерирование разрывов строки, ожидание передачи вывода, сброс входящей и исходящей очередей, приостановка и возобновление передачи данных между компьютером и терминалом). Другие функции позволяют получить имя терминала и проверить, ссылается ли на него заданный файловый дескриптор. С помощью системного вызова ioctl() можно выполнить ряд операций, связанных с терминалом, включая извлечение и изменение информации о размере его окна.

Дополнительная информация

В книге [Stevens, 1992] тоже описывается программирование терминалов и раскрывается гораздо больше подробностей о работе с последовательными портами. Этой теме посвящено также несколько сетевых ресурсов. В частности, на веб-сайте проекта LDP (www.tldp.org) находятся методические пособия Дэвида Лойера по текстовым терминалам и последовательным портам. Еще одним полезным источником информации является руководство по программированию последовательных портов для операционных систем POSIX Майкла Суита, доступное на www.easysw.com/~mike/serial/.

58.12. Упражнения

58.1. Реализуйте функцию isatty() (для этого вам может пригодиться описание функции tcgetattr() в разделе 58.2).

58.2. Реализуйте функцию ttyname().

58.3. Реализуйте функцию getpass(), описанную в разделе 8.5 (для получения файлового дескриптора управляющего терминала можно открыть файл /dev/tty).

58.4. Напишите программу, которая выводит информацию о том, в каком режиме работает терминал, связанный со стандартным вводом, — каноническом или неканоническом, и затем отображает значения TIME и MIN.

<p>59. Альтернативные модели ввода/вывода</p>

В данной главе будут рассмотрены три альтернативы традиционной модели файлового ввода/вывода, которую мы применяли в большинстве программ в этой книге:

• мультиплексированный ввод/вывод (системные вызовы select() и poll());

• ввод/вывод, основанный на сигналах;

• программный интерфейс epoll, доступный только в Linux.

59.1. Краткий обзор

Большинство программ, представленных на данный момент в этой книге, использует модель ввода/вывода, согласно которой процесс работает одновременно только с одним файловым дескриптором, и каждый системный вызов блокируется в ожидании передачи данных. Например, при чтении из канала вызов read() обычно останавливается, если в этом канале не обнаружено никаких данных; то же самое происходит и с вызовом write() при нехватке в канале места для записи. Аналогичное поведение проявляется при работе с файлами других типов, включая очереди FIFO и сокеты.

Дисковые файлы представляют собой особый случай. Как уже говорилось в главе 13, ядро использует буферный кэш, чтобы ускорить запросы ввода/вывода к диску. Следовательно, вызов write() возвращается сразу после передачи данных в буферный кэш ядра, не дожидаясь фактической их записи на диск (если только при открытии файла не был указан флаг O_SYNC). Соответственно, вызов read() передает данные из буферного кэша в пользовательский буфер, и если данных в кэше нет, то ядро приостанавливает процесс, считывая тем временем данные с диска.

Традиционной блокирующей модели ввода/вывода достаточно для большинства приложений, но не для всех. В частности, иногда может возникнуть необходимость в выполнении одной из следующих операций (или сразу обеих):

• проверка доступности ввода/вывода для файлового дескриптора; при этом, если ответ отрицательный, то операция не должна заблокироваться;

• мониторинг нескольких файловых дескрипторов для определения доступности ввода/вывода для любого из них.

Мы уже познакомились с двумя методиками, которые позволяют частично удовлетворить эти потребности: неблокирующий ввод/вывод и применение нескольких процессов или потоков.

Перейти на страницу:

Похожие книги