Интерфейс терминала POSIX - это обобщенная абстракция, включающая как интерфейс прикладного программирования для программ, так и набор поведенческих ожиданий для пользователей терминала, как определено стандартом POSIX и спецификацией единой Unix. Это историческое развитие терминальных интерфейсов BSD версии 4 и Seventh Edition Unix.
Множество устройств ввода-вывода рассматриваются как «терминалы» в системах Unix. Это включает:
В отличие от своих современников мэйнфреймов и миникомпьютеров, исходная система Unix была разработана исключительно для немых терминалов, и это остается так и по сей день. Терминал - это ориентированное на символы устройство, состоящее из потоков символов, полученных от устройства и отправленных на него. Хотя потоки символов структурированы, включая управляющие символы, управляющие коды и специальные символы, протокол ввода-вывода не структурирован, как протокол ввода-вывода интеллектуальных или интеллектуальных терминалов. Спецификации формата поля отсутствуют. Нет блочной передачи целых экранов (форм ввода) входных данных.
Напротив, мэйнфреймы и миникомпьютеры в закрытых архитектурах обычно используют блочно-ориентированные терминалы.
«Возможности» терминала включают в себя различные немые функции терминала, которые выходят за рамки того, что доступно с чистого телетайпа, которые программы могут использовать. Они (в основном) содержат escape-коды, которые могут быть отправлены на терминал или получены от него. Управляющие коды, отправляемые на терминал, выполняют различные функции, которые может выполнять терминал CRT (или программный эмулятор терминала), но не телетайп, например, перемещение курсора терминала в положение на экране, очистка и прокрутка всего или частей экрана., включение и выключение подключенных принтеров, программируемые функциональные клавиши, изменение цвета и атрибутов дисплея (например, обратное видео ) и установка строк заголовков дисплея. Управляющие коды, полученные от терминала означают такие вещи, как функциональной клавиши, клавиши со стрелками, а также другие специальные нажатия клавиш ( домашний ключ, конец ключ, помощь ключ, ключ PgUp, ключ PgDn, вставки ключа, удалить ключ, и так далее).
Эти возможности закодированы в базах данных, которые настраиваются системным администратором и доступны из программ через библиотеку terminfo (которая заменяет старую библиотеку termcap ), на основе которой, в свою очередь, создаются библиотеки, такие как библиотеки curses и ncurses. Прикладные программы используют возможности терминала для предоставления текстовых пользовательских интерфейсов с окнами, диалоговыми окнами, кнопками, метками, полями ввода, меню и т. Д.
TERM
et al. Конкретный набор возможностей для терминала, который использует (поддерживающая терминал) программа ввода и вывода, получен из базы данных, а не встроен в программы и библиотеки, и управляется TERM
переменной среды (и, необязательно, для библиотек termcap и terminfo, что TERMCAP
и TERMINFO
переменные среды, соответственно). Эта переменная устанавливается любой программой монитора терминала, которая порождает программы, которые затем используют этот терминал для ввода и вывода, а иногда и явно. Например:
TERM
переменную среды в соответствии с системной базой данных (по-разному, inittab или файлы конфигурации для программ ttymon или launchd ), определяя, какие локальные терминалы подключены к каким последовательным портам и какие типы терминалов предоставляются локальными виртуальными терминалами. или локальная системная консоль.TERM
переменную среды сразу после входа в систему на правильный тип. (Чаще тип терминала, установленный программой getty для коммутируемой линии, который системный администратор определил как наиболее часто используемый коммутируемыми пользователями с удаленными терминалами, совпадает с типом терминала, используемым коммутируемым пользователем и этому пользователю не нужно переопределять тип терминала.)TERM
переменные среды к тому же типа терминала в качестве клиента SSH.TERM
переменную среды, чтобы указать тип терминала, который он эмулирует. Эмулируемые терминалы часто не совсем соответствуют реальному терминальному оборудованию, и эмуляторы терминала имеют имена типов, предназначенные для их использования. Программа xterm (по умолчанию) устанавливает xterm
, например, тип терминала. Программа GNU Screen устанавливается screen
как тип терминала.Терминалы предоставляют средства контроля работы. В интерактивном режиме пользователь в терминале может отправлять управляющие символы, которые приостанавливают текущее выполняемое задание, возвращаясь к интерактивной оболочке управления заданием, которая породила задание, и может запускать команды, которые переводят задания в «фоновый режим» или переключают другое, фоновое задание. на передний план (при необходимости отключив его).
Строго говоря, в Unices терминальное устройство включает в себя базовый драйвер устройства tty, отвечающий за физический контроль оборудования устройства с помощью инструкций ввода-вывода и обработку запросов прерывания устройства для ввода и вывода символов, а также за дисциплину линии. Дисциплина линии не зависит от фактического оборудования устройства, и такая же дисциплина линии может использоваться для устройства концентратора терминалов, отвечающего за несколько управляющих терминалов, что и для псевдотерминала. Фактически, линейная дисциплина (или, в случае BSD, AIX и других систем, линейная дисциплина ) одинакова для всех оконечных устройств. Именно дисциплина линии отвечает за локальное эхо, редактирование строки, обработку режимов ввода, обработку режимов вывода и отображение символов. Все эти вещи не зависят от реального оборудования и работают так же, как и в простых абстракциях, предоставляемых драйверами устройств tty: передача символа, получение символа, установка различных состояний оборудования.
В Unix седьмой редакции, системах BSD и производных, включая macOS и Linux, каждое оконечное устройство может переключаться между несколькими линейными дисциплинами. В системе ATamp;T STREAMS дисциплины линий - это модули STREAMS, которые могут быть вставлены в стек ввода-вывода STREAMS и извлечены из него.
Терминальный интерфейс POSIX является производным от терминальных интерфейсов различных систем Unix.
Терминальный интерфейс, предоставляемый Unix 32V и Seventh Edition Unix, а также представленный BSD версии 4 как старый драйвер терминала, был простым, в основном ориентированным на телетайпы в качестве терминалов. Ввод вводился построчно, причем драйвер терминала в операционной системе (а не сами терминалы) предоставлял простые возможности редактирования строки. Ядро поддерживало буфер, в котором происходило редактирование. Приложения, читающие ввод с терминала, получат содержимое буфера только тогда, когда return на терминале будет нажата клавиша для завершения редактирования строки. @ Ключ, посланный от терминала к системе будет стереть ( «убить») все содержимые текущий буфер редактирования, и будет обычно отображаются как « @ символ», за которым следует последовательность перевода строки, чтобы переместить позицию печати на свежие заготовки линия. # Ключ, посланный от терминала к системе будет удалить последний символ из конца буфера редактирования, и будет обычно отображаются как « # символ», который пользователи будут должен признать, как обозначающие «rubout» предыдущий символ (телетайпы физически не способны стирать символы после того, как они были напечатаны на бумаге).
С точки зрения программирования, оконечное устройство имело скорости передачи и приема, символы «стирания» и «уничтожения» (которые выполняли редактирование строки, как объяснено), символы «прерывание» и «выход» (генерирование сигналов для всех процессы, для которых терминал был управляющим терминалом), символы «старт» и «стоп» (используемые для управления потоком модема ), символ «конец файла» (действующий как возврат каретки, за исключением того, что он удаляется из буфера read()
системным вызовом и следовательно, это может привести к возврату результата нулевой длины) и различные флаги базового режима, определяющие, эмулировалось ли локальное эхо драйвером терминала, было ли включено управление потоком модема, длительности различных задержек вывода, отображение символа возврата каретки, и три режима ввода.
Три режима ввода были:
В линейном режиме дисциплина линии выполняет все функции редактирования строки и распознает управляющие символы «прерывание» и «выход» и преобразует их в сигналы, отправляемые процессам. Прикладные программы, считывающие с терминала, получают целые строки после того, как редактирование строки было завершено нажатием клавиши возврата.
cbreak mode - это один из двух режимов по очереди. ( Стивен Р. Борн в шутку назвал его ( Bourne 1983, стр. 288) «полуготовым» и, следовательно, «редким» режимом.) Линия дисциплины не выполняет редактирования строки, а управляющие последовательности для функций редактирования строки обрабатываются как обычный ввод символов. Прикладные программы, считывающие с терминала, получают символы немедленно, как только они становятся доступными для чтения во входной очереди. Однако управляющие символы «прерывание» и «выход», а также символы управления потоком модема по-прежнему обрабатываются особым образом и удаляются из входного потока.
Программный интерфейс для запроса и изменения всех этих режимов и управляющих символов был ioctl()
системным вызовом. (Это заменило stty()
и gtty()
системные вызовы Шестого издания Unix.) Хотя «стирают» и «убивать» символы были изменяемые от их дефолтов # и @, на протяжении многих лет они были предварительно установленное по умолчанию в драйверах терминальных устройств, а также на многих Системы Unix, которые изменяли настройки терминального устройства только в процессе входа в систему, в сценариях входа в систему, которые запускались после того, как пользователь ввел имя пользователя и пароль, любые ошибки в запросах входа в систему и пароля должны были быть исправлены с использованием унаследованных символов ключа редактирования. с терминалов телетайпа.
Вместе с BSD Unices пришло управление заданиями и новый драйвер терминала с расширенными возможностями. Эти расширения содержат дополнительные (опять же программно изменяемые) специальные символы:
SUB
и EM
) вызвали генерацию нового SIGTSTP
сигнала для процессов в группе управляющих процессов терминала.ETB
, SYN
и DC2
) выполняются дополнительные строки редактирования функций. «стирание слова» стирает последнее слово в конце буфера редактирования строки. "literal next" позволяет ввести любой специальный символ в буфер редактирования строки (функция, доступная, что несколько неудобно, в Seventh Edition Unix через символ обратной косой черты). «повторная печать» заставляла дисциплину строки повторно печатать текущее содержимое буфера редактирования строки на новой строке (полезно, когда другой, фоновый процесс генерировал выходные данные, смешанные с редактированием строки). Программный интерфейс для запроса и изменения всех этих дополнительных режимов и управляющих символов все еще оставался ioctl()
системным вызовом, который его создатели ( Leffler et al. 1989, p. 262) описали как «довольно загроможденный интерфейс». Все исходные функциональные возможности Unix седьмого издания были сохранены, а новые функциональные возможности были добавлены с помощью дополнительных ioctl()
кодов операций, в результате чего программный интерфейс явно вырос, и это представило некоторое дублирование функциональности.
В System III был представлен новый программный интерфейс, который объединил отдельные ioctl()
операции Seventh Edition для получения и установки флагов, а также для получения и установки управляющих символов в вызовы, которые использовали termio
структуру для хранения как флагов, так и управляющих символов, которые могли получать их за одну операцию и устанавливать их. в другой одиночной операции. Он также разделил некоторые флаги интерфейса Seventh Edition на несколько отдельных флагов и добавил некоторые дополнительные возможности, хотя он не поддерживал управление заданиями или усовершенствования готового режима 4BSD. Например, он заменил режимы «приготовленный», «cbreak» и «сырой» седьмого издания другими абстракциями. Распознавание символов, генерирующих сигнал, не зависит от режима ввода, и есть только два режима ввода: канонический и неканонический. (Это позволяет использовать режим терминального ввода, отсутствующий в Seventh Edition и BSD: канонический режим с отключенной генерацией сигнала.)
Преемники System III, включая System V, использовали тот же интерфейс.
Одной из основных проблем, которую решил стандарт POSIX с помощью определения общего терминального интерфейса, было множество программных интерфейсов. Хотя ко времени появления стандарта поведение терминалов было довольно единообразным от системы к системе, большинство Unix приняло концепции линейной дисциплины и возможности управления заданиями BSD, программный интерфейс к терминалам через ioctl()
системный вызов был беспорядочным. Разные Unices предоставляют разные ioctl()
операции с разными (символическими) именами и разными флагами. Переносимый исходный код должен был содержать значительный объем условной компиляции, чтобы учесть различия между программными платформами, даже если все они были условно Unix.
Стандарт POSIX ioctl()
полностью заменяет систему набором библиотечных функций (которые, конечно, могут быть реализованы под обложками с помощью специфичных для платформы ioctl()
операций) со стандартизованными именами и параметрами. Структура termio
данных System V Unix использовалась в качестве шаблона для termios
структуры данных POSIX, поля которой в основном не изменились, за исключением того, что теперь они использовали псевдонимы типов данных для указания полей, что позволяло разработчикам легко переносить их на несколько процессорных архитектур. чем явно требуя от unsigned short
и char
данных типов с и C ++ языки программирования (которые могут быть неудобными размерами на некоторых процессорных архитектурах).
POSIX также представил поддержку управления заданиями со termios
структурой, содержащей символы приостановки и отложенной приостановки в дополнение к управляющим символам, поддерживаемым System III и System V. Он не добавил никаких расширений готового режима из BSD, хотя SunOS 4. x, System V Release 4, Solaris, HP-UX, AIX, новые BSD, macOS и Linux реализовали их как расширения для termios
.
Каждый процесс в системе имеет либо один управляющий терминал, либо вообще не имеет управляющего терминала. Процесс наследует свой управляющий терминал от своего родителя, и единственными операциями над процессом являются получение управляющего терминала процессом, у которого нет управляющего терминала, и отказ от него процессом, у которого есть управляющий терминал.
Не определен переносимый способ получения управляющего терминала, метод определяется реализацией. Стандарт определяет O_NOCTTY
флаг для open()
системного вызова, который является способом предотвращения того, что в противном случае является обычным способом получения управляющего терминала (процесс без управляющего open()
терминала, файл терминального устройства, который еще не является управляющим терминалом для какого-либо другого процесса., без указания O_NOCTTY
флага), но оставляет его обычную семантику необязательной.
Каждый процесс также является членом группы процессов. Каждое оконечное устройство записывает группу процессов, которая называется его группой процессов переднего плана. Группы процессов управляют доступом к терминалам и доставкой сигналов. Сигналы, генерируемые терминалом, отправляются всем процессам, которые являются членами группы процессов переднего плана терминала. read()
и write()
операции ввода-вывода на терминале, выполняемые процессом, который не является членом группы процессов переднего плана терминала, будут и могут необязательно (соответственно) вызывать отправку сигналов ( SIGTTIN
и SIGTTOU
соответственно) вызывающему процессу. Различные библиотечные функции, изменяющие режим терминала, ведут себя так же, как и другие write()
, за исключением того, что они всегда генерируют сигналы, даже если эта функция отключена write()
сама по себе.
termios
данных Структура данных, используемая всеми вызовами терминальной библиотеки, представляет собой termios
структуру, определение которой на языках программирования C и C ++ выглядит следующим образом:
struct termios { tcflag_t c_iflag ; // Input modes tcflag_t c_oflag ; // Output modes tcflag_t c_cflag ; // Control modes tcflag_t c_lflag ; // Local modes cc_t c_cc[NCCS] ; // Control characters } ;
Порядок полей в termios
структуре не определен, и реализациям разрешено добавлять нестандартные поля. Действительно, реализации должны добавлять нестандартные поля для записи входных и выходных скоростей передачи. Они записываются в структуре в форме, определяемой реализацией, и доступны через функции доступа, а не путем прямого манипулирования значениями полей, как в случае со стандартизованными полями структуры.
Псевдонимы типов данных tcflag_t
и cc_t
, а также символические константы NCCS
и символьные константы для различных флагов режима, имен управляющих символов и скорости передачи данных определены в стандартном заголовке termios.h
. (Его не следует путать с заголовком termio.h
с аналогичным названием из System III и System V, который определяет аналогичную termio
структуру и множество символьных констант с аналогичными именами. Этот интерфейс специфичен для System III и System V, и код, который его использует, будет не обязательно быть переносимым в другие системы.)
Поля структуры (вкратце, подробности см. В основной статье):
c_iflag
c_oflag
c_cflag
c_lflag
SIGTTOU
сигнала write()
системным вызовомФункции библиотеки (вкратце, подробности см. В основной статье):
tcgetattr()
termios
структуреtcsetattr()
termios
структуры, при желании ожидая вывода из очереди для слива и очистки ввода из очередиcfgetispeed()
termios
структуреcfgetospeed()
termios
структуреcfsetispeed()
termios
структуреcfsetospeed()
termios
структуреtcsendbreak()
tcdrain()
tcflush()
tcflow()
tcgetpgrp()
tcsetpgrp()
Поле | имея в виду | Получено read() | Заметки |
---|---|---|---|
c_cc[VEOF] | конец файла | Нет | Обработано только при редактировании строки в каноническом режиме |
c_cc[VEOL] | конец линии | да | Обработано только при редактировании строки в каноническом режиме |
c_cc[VERASE] | "стереть" | Нет | Обработано только при редактировании строки в каноническом режиме |
c_cc[VKILL] | "убийство" | Нет | Обработано только при редактировании строки в каноническом режиме |
c_cc[VINTR] | "прерывать" | Нет | Характер генерации сигнала независимо от режима ввода |
c_cc[VQUIT] | "покидать" | Нет | Характер генерации сигнала независимо от режима ввода |
c_cc[VSUSP] | "приостановить" | Нет | Характер генерации сигнала независимо от режима ввода |
c_cc[VSTOP] | "останавливаться" | Нет | Символ управления потоком модема, не зависящий от режима ввода |
c_cc[VSTART] | "Начало" | Нет | Символ управления потоком модема, не зависящий от режима ввода |
c_cc[]
Элемент массива в termios
структуре данных определяет все (программно изменяемых) специальных символов. Индексы в массиве представляют собой символьные константы, по одной для каждого специального типа символа, как в таблице справа. (Еще две записи в массиве относятся к обработке ввода в неканоническом режиме и обсуждаются ниже.)
Непрограммно изменяемые специальные символы - это перевод строки (ASCII LF
) и возврат каретки (ASCII CR
).
Обработка ввода определяет поведение read()
системного вызова на оконечном устройстве, а также характеристики линейного редактирования и генерации сигналов. В отличие от седьмого издания Unix и BSD версии 4 и, как и в случае с System III и System V, редактирование строки работает в одном из двух режимов: каноническом режиме и неканоническом режиме. Основное различие между ними состоит в том, что с точки зрения требований блокировки / неблокирования read()
системного вызова (указанного O_NONBLOCK
флажком в файловом дескрипторе через open()
или fcntl()
) данные «доступны для чтения».
В каноническом режиме данные накапливаются в буфере редактирования строки и не становятся «доступными для чтения» до тех пор, пока редактирование строки не будет прекращено пользователем (на терминале), отправившим символ-разделитель строк. Символы-разделители строк являются специальными символами, и это конец файла, конец строки и перевод строки (ASCII LF
). Первые два настраиваются программно, а последний фиксирован. Последние два включены в буфер редактирования строки, а первый - нет.
Точнее, в буфере редактирования строк накапливается ноль или более строк, разделенных разделителями строк (которые могут или не могут быть отброшены после read()
их чтения), а редактирование строки работает с той частью буфера редактирования строки, которая следует за последний (если есть) разделитель строк в буфере. Так, например, символ «стирания» (какой бы он ни был запрограммирован) удалит последний символ в строковом буфере только до (но не включая) разделителя предыдущей строки.
В неканоническом режиме данные накапливаются в буфере (который может быть или не быть буфером редактирования строки - некоторые реализации имеют отдельные очереди «обработанный ввод» и «необработанный ввод») и становятся «доступными для чтения» в соответствии со значениями. из двух управляющих параметров ввода, c_cc[MIN]
и c_cc[TIME]
членов termios
структуры данных. Обе величины являются беззнаковыми (поскольку cc_t
для беззнакового типа требуется псевдоним). Первый определяет минимальное количество символов, а второй указывает тайм-аут в десятых долях секунды. Есть четыре возможности:
c_cc[TIME]
и c_cc[MIN]
оба равны нулюread()
возвращаются с любыми данными, находящимися в буфере (потенциально возвращая ноль, если есть нулевые доступные данные).c_cc[TIME]
ненулевой и c_cc[MIN]
равен нулюread()
системного вызова или при получении одного символа. Другими словами, read()
ожидает в течение максимального указанного общего времени и может вернуть нулевые данные, а также возвращает любые данные, как только они будут получены.c_cc[TIME]
равен нулю и c_cc[MIN]
не равен нулюread()
ожидает минимального объема данных (который может быть больше, чем то, что вызывающий абонент готов прочитать в системном вызове), не вернет нулевые данные и может ждать бесконечно.c_cc[TIME]
и c_cc[MIN]
оба ненулевыеread()
ожидает минимального объема данных (который может быть больше, чем то, что вызывающий абонент готов прочитать в системном вызове), не будет возвращать нулевые данные, может ждать бесконечно, но не будет ждать дольше указанного тайм-аута. если в буфере для чтения есть хотя бы один символ.Обработка вывода практически не изменилась по сравнению с ее корнями в System III / System V. Флаги управления режимом вывода определяют различные варианты: