stringtranslate.com

Терминальный интерфейс POSIX

Терминальный интерфейс POSIX — это обобщенная абстракция, включающая как интерфейс прикладного программирования для программ, так и набор поведенческих ожиданий для пользователей терминала , как определено стандартом POSIX и Single Unix Specification . Это историческое развитие терминальных интерфейсов BSD версии 4 и Seventh Edition Unix .

Общие базовые концепции

Аппаратное обеспечение

Множество устройств ввода-вывода рассматриваются как «терминалы» в системах Unix. [1] [2] К ним относятся:

Терминальный интеллект и возможности

Интеллект: терминалы тупые, а не умные

В отличие от своих современников — мэйнфреймов и мини-компьютеров [ требуется ссылка ] — оригинальная система Unix была разработана исключительно для немых терминалов, и это остается таковым и по сей день. [6] Терминал — это символьно-ориентированное устройство, включающее потоки символов, получаемых с устройства и отправляемых на него. [6] [7] Хотя потоки символов структурированы, включая управляющие символы , управляющие коды и специальные символы, протокол ввода-вывода не структурирован так, как протокол ввода-вывода интеллектуальных терминалов . Спецификации формата полей отсутствуют. Блоковая передача целых экранов (форм ввода) входных данных отсутствует.

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

Возможности: terminfo, termcap, проклятия и др.

«Возможности» терминала включают в себя различные функции немого терминала, которые выходят за рамки того, что доступно чистому телетайпу, которые могут использовать программы. Они (в основном) включают в себя управляющие коды, которые могут быть отправлены или получены с терминала. Управляющие коды, отправленные на терминал, выполняют различные функции, которые терминал с ЭЛТ (или программный эмулятор терминала) может выполнять, но не может телетайп, такие как перемещение курсора терминала в позиции на экране, очистка и прокрутка всего или части экрана, включение и выключение подключенных устройств принтера, программируемые функциональные клавиши, изменение цветов и атрибутов дисплея (например, обратное видео ) и настройка строк заголовков дисплея. Управляющие коды, полученные с терминала, обозначают такие вещи, как функциональная клавиша , клавиша со стрелкой и другие специальные нажатия клавиш ( клавиша «Домой» , клавиша «Завершить» , клавиша «Справка » , клавиша «PgUp» , клавиша «PgDn» , клавиша «Вставить » , клавиша «Удалить » и т. д.). [8] [9]

Эти возможности закодированы в базах данных, которые настраиваются системным администратором и доступны из программ через библиотеку terminfo (которая заменяет старую библиотеку termcap ), на которой, в свою очередь, построены библиотеки, такие как библиотеки curses и ncurses . Прикладные программы используют возможности терминала для предоставления текстовых пользовательских интерфейсов с окнами, диалоговыми окнами, кнопками, метками, полями ввода, меню и т. д. [10] [11]

Управление переменными среды: TERMи др.

Конкретный набор возможностей для терминала, который использует ввод и вывод (терминально-ориентированной) программы, получается из базы данных, а не жестко зашит в программы и библиотеки, и управляется TERM переменной окружения (и, опционально для библиотек termcap и terminfo, переменными окружения TERMCAPи TERMINFOсоответственно). [10] Эта переменная устанавливается любой программой монитора терминала, которая порождает программы, которые затем используют этот терминал для ввода и вывода, или иногда явно. Например:

Контроль работы

Терминалы предоставляют средства управления заданиями. В интерактивном режиме пользователь на терминале может отправлять управляющие символы, которые приостанавливают текущую работу, возвращаясь к интерактивной оболочке управления заданиями, которая породила задание, и может запускать команды, которые помещают задания в «фоновый» режим или переключают другое фоновое задание на передний план (отменяя его при необходимости). [14] [15]

Линейные дисциплины

Строго говоря, в Unix-системах терминальное устройство включает в себя базовый драйвер устройства tty , отвечающий за физическое управление оборудованием устройства с помощью инструкций ввода-вывода и обработку запросов прерывания устройства для ввода и вывода символов, а также дисциплину линии . Дисциплина линии не зависит от фактического оборудования устройства, и та же дисциплина линии может использоваться для терминального концентратора , отвечающего за несколько управляющих терминалов, как и для псевдотерминала. Фактически, дисциплина линии (или, в случае BSD, AIX и других систем, дисциплины линии ) одинаковы для всех терминальных устройств. Именно дисциплина линии отвечает за локальное эхо, редактирование строк, обработку режимов ввода, обработку режимов вывода и отображение символов. Все эти вещи не зависят от фактического оборудования, работая так, как они делают в простых абстракциях, предоставляемых драйверами устройств tty: передают символ, получают символ, устанавливают различные состояния оборудования. [16] [17]

В системах Seventh Edition Unix , BSD и производных, включая macOS и Linux , каждое терминальное устройство может переключаться между несколькими дисциплинами линий. [18] В системе AT&T STREAMS дисциплины линий представляют собой модули STREAMS, которые могут быть помещены в стек ввода-вывода STREAMS и извлечены из него. [19]

История

Терминальный интерфейс POSIX создан на основе терминальных интерфейсов различных систем Unix.

Ранние Unix-системы: седьмое издание Unix

Терминальный интерфейс, предоставляемый Unix 32V и Seventh Edition Unix, а также представленный BSD версии 4 как старый драйвер терминала , был простым, в значительной степени ориентированным на телетайпы в качестве терминалов. Ввод осуществлялся построчно, при этом драйвер терминала в операционной системе (а не сами терминалы) предоставлял простые возможности редактирования строк. Ядро поддерживало буфер, в котором происходило редактирование. Приложения, считывающие ввод с терминала, получали содержимое буфера только при returnнажатии клавиши на терминале для завершения редактирования строки. Клавиша, отправленная с терминала в систему, стирала («убивала») все текущее содержимое буфера редактирования и обычно отображалась как символ « @ », за которым следовала последовательность новой строки для перемещения позиции печати на новую пустую строку. Ключ , отправленный с терминала в систему, стирает последний символ в конце буфера редактирования и обычно отображается как символ « # », который пользователи должны распознавать как «стирание» предыдущего символа (телетайпы физически не способны стирать символы после того, как они были напечатаны на бумаге). [20] [21] [22] [23] [18]@#

С точки зрения программирования, терминальное устройство имело скорости передачи и приема данных , символы «стирания» и «уничтожения» (которые выполняли редактирование строк, как объяснялось), символы «прерывания» и «выхода» (генерирующие сигналы для всех процессов, для которых терминал был управляющим терминалом), символы «старта» и «стопа» (используемые для управления потоком модема ), символ «конца файла» (действующий как возврат каретки, за исключением того, что он отбрасывается из буфера системным read()вызовом и, следовательно, потенциально приводит к возврату результата нулевой длины) и различные флаги базового режима , определяющие, эмулируется ли локальное эхо драйвером терминала ядра, включено ли управление потоком модема, длины различных задержек вывода, отображение для символа возврата каретки и три режима ввода. [24]

Три режима ввода были следующими:

режим линии (также называемый «приготовленным» режимом)

В линейном режиме дисциплина линии выполняет все функции редактирования строк и распознает управляющие символы «прерывание» и «выход» и преобразует их в сигналы, отправляемые процессам. Прикладные программы, считывающие данные с терминала, получают целые строки после того, как редактирование строк было завершено пользователем, нажавшим клавишу возврата. [21] [25]

режим cbreak

Режим cbreak — один из двух режимов «символ за раз». ( Стивен Р. Борн в шутку назвал его (Bourne 1983, стр. 288) «полуготовым» и, следовательно, «редким» режимом.) Дисциплина строки не выполняет редактирование строк, а управляющие последовательности для функций редактирования строк обрабатываются как обычный ввод символов. Прикладные программы, считывающие с терминала, получают символы немедленно, как только они становятся доступны в очереди ввода для чтения. Однако управляющие символы «прерывание» и «выход», а также символы управления потоком модема по-прежнему обрабатываются особым образом и удаляются из входного потока. [26] [27]

сырой режим
Режим raw — это другой из двух режимов «символ за раз». Дисциплина строки не выполняет редактирование строки, а управляющие последовательности для функций редактирования строки и различных специальных символов («прерывание», «выход» и управление потоком) обрабатываются как обычный ввод символов. Прикладные программы, считывающие с терминала, получают символы немедленно и получают весь поток символов без изменений, как будто он пришел с самого терминального устройства. [28] [26] [27]

Программным интерфейсом для запроса и изменения всех этих режимов и управляющих символов был ioctl()системный вызов . (Он заменил системные вызовы stty()и gtty()шестого издания Unix.) [29] [30] Хотя символы «erase» и «kill» можно было изменять из их значений по умолчанию и , в течение многих лет они были предустановленными значениями по умолчанию в драйверах терминальных устройств и во многих системах Unix, которые изменяли только настройки терминального устройства как часть процесса входа в систему, в сценариях входа в систему, которые запускались после того, как пользователь вводил имя пользователя и пароль, любые ошибки в приглашениях на вход в систему и пароль приходилось исправлять с помощью исторических символов клавиш редактирования, унаследованных от терминалов телетайпа. [23]#@

BSD: возникновение контроля над работой

С появлением BSD Unix появился контроль заданий и новый драйвер терминала с расширенными возможностями. [18] Эти расширения включали дополнительные (опять же программно-изменяемые) специальные символы:

Программный интерфейс для запроса и изменения всех этих дополнительных режимов и управляющих символов по-прежнему был ioctl()системным вызовом, который его создатели (Leffler et al. 1989, p. 262) описали как «довольно загроможденный интерфейс». Вся первоначальная функциональность седьмого издания Unix была сохранена, а новая функциональность была добавлена ​​через дополнительные ioctl()коды операций, в результате чего программный интерфейс явно вырос и представлял собой некоторое дублирование функциональности. [31]

Система III и Система V

System III представила новый программный интерфейс, который объединил отдельные операции Seventh Edition ioctl()для получения и установки флагов и для получения и установки управляющих символов в вызовы, которые использовали termioструктуру для хранения как флагов, так и управляющих символов, и которые могли получать их в одной операции и устанавливать их в другой одной операции. Он также разделил некоторые флаги из интерфейса Seventh Edition на несколько отдельных флагов и добавил некоторые дополнительные возможности, хотя он не поддерживал управление заданиями или улучшения приготовленного режима 4BSD. [32] Например, он заменил режимы "cooked", "cbreak" и "raw" Seventh Edition на другие абстракции. Распознавание генерирующих сигналы символов не зависит от режима ввода, и есть только два режима ввода: канонический и неканонический. (Это позволяет использовать терминальный режим ввода, отсутствующий в Seventh Edition и BSD: канонический режим с отключенной генерацией сигнала.)

Последователи System III, включая System V , использовали тот же интерфейс.

POSIX: Консолидация и абстракция

Одной из основных проблем, которую стандарт POSIX решил своим определением общего терминального интерфейса, было изобилие программных интерфейсов. Хотя ко времени стандарта поведение терминалов было довольно единообразным от системы к системе, большинство Unix-систем переняли понятия дисциплин линий и возможности управления заданиями BSD, программный интерфейс к терминалам через ioctl()системный вызов был беспорядочным. Различные Unix-системы предоставляли разные ioctl()операции с разными (символическими) именами и разными флагами. Переносимый исходный код должен был содержать значительный объем условной компиляции для учета различий между программными платформами, хотя все они были теоретически Unix. [33]

Стандарт POSIX ioctl()полностью заменяет систему набором библиотечных функций (которые, конечно, могут быть реализованы под прикрытием посредством ioctl()операций, специфичных для платформы) со стандартизированными именами и параметрами. termioСтруктура данных System V Unix использовалась в качестве шаблона для termiosструктуры данных POSIX, поля которой в значительной степени не изменились, за исключением того, что теперь они использовали псевдонимы типов данных для указания полей, что позволяет разработчикам легко переносить их на несколько архитектур процессоров, а не явно требовать типы данных unsigned shortи charязыков программирования C и C++ (которые могут иметь неудобные размеры на некоторых архитектурах процессоров). [33] [34]

В POSIX также появилась поддержка управления заданиями, при этом termiosструктура содержала символы приостановки и отложенной приостановки в дополнение к управляющим символам, поддерживаемым System III и System V. В нее не было добавлено никаких расширений режима подготовки из BSD, хотя SunOS 4.x, System V Release 4 , Solaris , HP-UX , AIX , более новые BSD, macOS и Linux реализовали их как расширения для termios.

Что определяет стандарт

Управление терминалами и группами процессов

Каждый процесс в системе имеет либо один управляющий терминал , либо вообще не имеет управляющего терминала. Процесс наследует свой управляющий терминал от своего родителя, и единственными операциями над процессом являются получение управляющего терминала процессом, у которого нет управляющего терминала, и освобождение его процессом, у которого есть управляющий терминал. [33]

Не определен переносимый способ получения управляющего терминала, метод определяется реализацией. Стандарт определяет флаг O_NOCTTYдля open()системного вызова , который является способом предотвращения того, что в противном случае является обычным способом получения управляющего терминала (процесс без управляющего терминала open()sa файл устройства терминала, который уже не является управляющим терминалом для какого-либо другого процесса, без указания O_NOCTTYфлага [35] ), но оставляет его традиционную семантику необязательной.

Каждый процесс также является членом группы процессов. Каждое терминальное устройство записывает группу процессов, которая называется его группой процессов переднего плана . Группы процессов управляют доступом к терминалу и доставкой сигналов. Сигналы, генерируемые на терминале, отправляются всем процессам, которые являются членами группы процессов переднего плана терминала. read()и write()операции ввода-вывода на терминале процессом, который не является членом группы процессов переднего плана терминала, будут и могут опционально (соответственно) вызывать сигналы ( SIGTTINи SIGTTOUсоответственно), которые будут отправлены вызывающему процессу. Различные библиотечные функции изменения режима терминала ведут себя так же, как write(), за исключением того, что они всегда генерируют сигналы, даже если эта функциональность отключена для write()себя. [36] [37]

termiosСтруктура данных

Структура данных, используемая всеми вызовами терминальной библиотеки, представляет собой termiosструктуру, [38] определение которой на языках программирования C и C++ выглядит следующим образом: [34]

struct termios { tcflag_t c_iflag ; // Режимы ввода tcflag_t c_oflag ; // Режимы вывода tcflag_t c_cflag ; // Режимы управления tcflag_t c_lflag ; // Локальные режимы cc_t c_cc [ NCCS ] ; // Управляющие символы } ;                       

Порядок полей в termiosструктуре не определен, и реализациям разрешено добавлять нестандартные поля. [34] Действительно, реализации должны добавлять нестандартные поля для записи входных и выходных скоростей передачи данных. Они записываются в структуру в форме, определяемой реализацией, и к ним осуществляется доступ через функции доступа, а не путем прямого манипулирования значениями полей, как в случае со стандартизированными полями структуры. [39]

Псевдонимы типов данных tcflag_tи cc_t, а также символическая константа NCCSи символические константы для различных флагов режима, имен управляющих символов и скоростей передачи данных определяются в стандартном заголовке termios.h. (Не следует путать его с заголовком с аналогичным названием termio.hиз System III и System V, который определяет похожую termioструктуру и множество похожих по названию символических констант. Этот интерфейс специфичен для System III и System V, и код, который его использует, не обязательно будет переносимым на другие системы.) [40]

Поля структуры следующие (вкратце, подробности см. в основной статье [ необходимы пояснения ] ):

c_iflag
Флаги режима ввода для управления четностью ввода, переводом новой строки ввода, управлением потоком модема , 8-битной чистотой и реакцией на состояние «разрыва» (последовательного порта) [34]
c_oflag
Флаги режима вывода для управления постобработкой вывода, определяемой реализацией, переводом новой строки вывода и задержками вывода после отправки различных управляющих символов [41] [27]
c_cflag
Флаги управления аппаратным обеспечением терминала для управления фактическим терминальным устройством, а не дисциплиной линии: количество бит в символе, тип четности, управление зависанием и управление потоком последовательной линии [42]
c_lflag
локальные флаги управления для управления дисциплиной линии, а не аппаратным обеспечением терминала: канонический режим, эхо-режимы, распознавание и обработка символов генерации сигнала, а также включение генерации сигнала SIGTTOUсистемным write()вызовом [39]

Функции библиотеки следующие (кратко, подробности см. в основной статье [ необходимы пояснения ] ):

tcgetattr()
запросить текущие настройки атрибутов терминального устройства в termiosструктуру [43]
tcsetattr()
установить текущие настройки атрибутов терминального устройства из termiosструктуры, опционально ожидая слива очереди вывода и очищая очередь ввода [43]
cfgetispeed()
запросить входную скорость передачи данных из полей, определенных реализацией в termiosструктуре [44]
cfgetospeed()
запросить выходную скорость передачи данных из полей, определенных реализацией в termiosструктуре [44]
cfsetispeed()
установить скорость передачи данных на входе в полях, определяемых реализацией в termiosструктуре [44]
cfsetospeed()
установить выходную скорость передачи данных в полях, определяемых реализацией в termiosструктуре [44]
tcsendbreak()
отправить сигнал «прерывания» модема на последовательный терминал устройства [45]
tcdrain()
дождаться слива очереди вывода [45]
tcflush()
отменить ввод в очереди [45]
tcflow()
управление потоком изменений [45]
tcgetpgrp()
запросить группу процессов переднего плана терминала [46]
tcsetpgrp()
установить группу процессов переднего плана терминала [46]

Специальные символы

Элемент c_cc[]массива termiosструктуры данных определяет все (программно-изменяемые) специальные символы. Индексы в массиве являются символическими константами, по одному для каждого типа специального символа, как в таблице справа. (Две дальнейшие записи в массиве относятся к обработке ввода неканонического режима и обсуждаются ниже.) [43]

Непрограммно модифицируемые специальные символы — это перевод строки (ASCII LF) и возврат каретки (ASCII CR). [47]

Обработка входных данных

Обработка ввода определяет поведение системного read()вызова на терминальном устройстве, а также редактирование строки и характеристики генерации сигнала дисциплины строки. В отличие от случая с седьмым изданием Unix и BSD версии 4, и как в случае с System III и System V, редактирование строки работает в одном из двух режимов: каноническом режиме и неканоническом режиме. Основное различие между ними заключается в том, когда с точки зрения требований блокирования/неблокирования системного read()вызова (указанных флагом O_NONBLOCKв дескрипторе файла через open()или fcntl()) данные «доступны для чтения». [48]

Обработка канонического режима

В каноническом режиме данные накапливаются в буфере редактирования строк и не становятся «доступными для чтения» до тех пор, пока редактирование строк не будет прекращено пользователем (на терминале), отправив символ-разделитель строк . Символы-разделители строк являются специальными символами, и это конец файла , конец строки и перевод строки (ASCII LF). Первые два устанавливаются программно, в то время как последний фиксирован. Последние два включены в буфер редактирования строк, в то время как первый — нет. [49]

Строго говоря, в буфере редактирования строк накапливается ноль или более строк, разделенных разделителями строк (которые могут быть или не быть отброшены, как только дело read()дойдет до их чтения), и редактирование строк работает с частью буфера редактирования строк, которая следует за последним (если таковой имеется) разделителем строк в буфере. Так, например, символ «стирания» (каким бы он ни был запрограммирован) сотрет последний символ в буфере строк только до (но не включая) предыдущего разделителя строк. [49]

Обработка неканонического режима

В неканоническом режиме данные накапливаются в буфере (который может быть или не быть буфером редактирования строки — некоторые реализации имеют отдельные очереди «обработанного ввода» и «сырого ввода») и становятся «доступными для чтения» в соответствии со значениями двух параметров управления вводом, c_cc[MIN]и c_cc[TIME]членов структуры termiosданных. Оба являются беззнаковыми величинами (потому что cc_tтребуется быть псевдонимом для беззнакового типа). Первый указывает минимальное количество символов, а второй указывает тайм-аут в десятых долях секунды. [50] Существует четыре возможности:

c_cc[TIME]и c_cc[MIN]оба равны нулю
В этом случае данные в буфере «доступны для чтения» немедленно и read()немедленно возвращаются с любыми данными, находящимися в буфере (потенциально возвращая ноль, если доступных данных нет). [51]
c_cc[TIME]не равен нулю и c_cc[MIN]равен нулю
В этом случае данные в буфере "доступны для чтения" после истечения указанного тайм-аута, таймер запускается началом системного read()вызова или при получении одного символа. Другими словами, read()ожидает максимальное указанное общее время и может вернуть нулевые данные, а также возвращает любые данные сразу после их получения. [51]
c_cc[TIME]равно нулю и c_cc[MIN]не равно нулю
В этом случае данные в буфере «доступны для чтения» после того, как указанное количество символов было получено в буфере. Другими словами, read()ждет минимального количества данных (которое может быть больше, чем то, что вызывающий готов прочитать в системном вызове), не вернет нулевые данные и может ждать бесконечно. [51]
c_cc[TIME]и c_cc[MIN]оба не равны нулю
В этом случае данные в буфере «доступны для чтения» после того, как в буфер поступило указанное количество символов или истек тайм-аут с момента ввода последнего символа. Для самого первого символа тайм-аута нет. Другими словами, read()ждет минимального количества данных (которое может быть больше, чем то, что вызывающий готов прочитать в системном вызове), не вернет нулевые данные, может ждать бесконечно, но не будет ждать дольше указанного тайм-аута, если в буфере есть хотя бы один символ для чтения. [51]

Обработка выходных данных

Обработка вывода в значительной степени не изменилась по сравнению с корнями System III/System V. Флаги управления режимом вывода определяют различные параметры:

Примечания

  1. ^ abc Christian 1988, стр. 11.
  2. Борн 1983, стр. 6.
  3. ^ Коффин 1991, стр. 820.
  4. Коффин 1991, стр. 23–24.
  5. ^ Леффлер и др. 1989, с. 259.
  6. ^ ab Coffin 1991, стр. 24.
  7. ^ Леффлер и др. 1989, с. 37–38.
  8. ^ Афзал 2008, стр. 419.
  9. ^ Фриш 2002, стр. 770.
  10. ^ ab Coffin 1991, стр. 115.
  11. ^ Коффин 1991, стр. 372.
  12. Коффин 1991, стр. 779.
  13. Коффин 1991, стр. 751–752.
  14. ^ Леффлер и др. 1989, с. 265.
  15. ^ Леффлер и др. 1989, с. 103.
  16. ^ Леффлер и др. 1989, стр. 38.
  17. ^ Леффлер и др. 1989, с. 260–261.
  18. ^ abc Леффлер и др. 1989, стр. 262.
  19. Кристиан 1988, стр. 395.
  20. Борн 1983, стр. 8.
  21. ^ ab Bourne 1983, стр. 130–131.
  22. ^ ab Bourne 1983, стр. 287.
  23. ^ ab Christian 1988, стр. 26.
  24. Борн 1983, стр. 132–133.
  25. ^ Леффлер и др. 1989, с. 259–260.
  26. ^ ab Bourne 1983, стр. 288.
  27. ^ abcdefg Леффлер и др. 1989, с. 260.
  28. Борн 1983, стр. 132.
  29. Борн 1983, стр. 133.
  30. Кристиан 1988, стр. 393.
  31. ^ Леффлер и др. 1989, с. 262–263.
  32. ^ "System III tty(4) man page source" . Получено 5 октября 2012 г. .
  33. ^ abc Zlotnick 1991, стр. 157.
  34. ^ abcd Злотник 1991, стр. 163.
  35. Борн 1983, стр. 130.
  36. ^ Злотник 1991, стр. 158.
  37. ^ Злотник 1991, стр. 173–174.
  38. ^ Злотник 1991, стр. 162.
  39. ^ ab Zlotnick 1991, стр. 166.
  40. ^ Злотник 1991, стр. 162–163.
  41. ^ Злотник 1991, стр. 164.
  42. ^ Злотник 1991, стр. 165.
  43. ^ abc Zlotnick 1991, стр. 167.
  44. ^ abcde Злотник 1991, стр. 169.
  45. ^ abcd Злотник 1991, стр. 172.
  46. ^ ab Zlotnick 1991, стр. 174.
  47. ^ ab Zlotnick 1991, стр. 159.
  48. ^ Злотник 1991, стр. 160.
  49. ^ ab Zlotnick 1991, стр. 160–161.
  50. ^ Злотник 1991, стр. 161.
  51. ^ abcd Злотник 1991, стр. 161–162.
  52. Борн 1983, стр. 287–288.

Источники

Дальнейшее чтение