Диаграмма показывает отношения между пользовательской программой, ядром системы и портом ввода/вывода при работе с физическим терминалом.
Физические терминалы присоединяются к компьютеру через аппаратный интерфейс ввода/вывода, называемый портом. Порты, сделанные различными изготовителями, устроены по разному, но поддерживают стандартные протоколы обмена, обычно стандарт RS232. В IBM PC-совместимых компьютерах терминальные порты RS232 называются COM-порты. Эти порты обычно конфигурируются или программируются, так что они могут работать с различными терминалами и на различных скоростях.
Пользовательские программы не имеют прямого доступа к портам ввода/вывода. Программный интерфейс с портами предоставляется драйвером устройства. Драйвер устройства — это модуль ядра Unix, который предоставляет набор аппаратно-зависимых функций, которые, в свою очередь, управляют портами и осуществляют доступ к ним. Доступ к драйверу происходит через специальный байт-ориентированный файл терминала. Имена этих файлов находятся в директории, обычно /dev или /dev/term, и доступ к ним может осуществляться так же, как к обычным файлам. Ввод и вывод на терминал осуществляется чтением и записью в соответствующий специальный файл. Многие программы не видят разницы между терминалом и обычным файлом, что позволяет перенаправлять ввод-вывод таких программ на терминал или в файл в зависимости от потребностей пользователя.
В Unix SVR4, терминальные драйверы сами имеют модульную структуру и состоят из нескольких модулей STREAMS, STREAMS — это появившийся в Unix SVR3 асинхронный интерфейс для драйверов последовательных (байт-ориентированных) устройств. API для разработки драйверов в Solaris описано в секции руководства 9 и не будет подробно обсуждаться в этом курсе.
Терминальный драйвер STREAMS состоит из, как минимум, двух модулей: собственно драйвера порта, который обеспечивает прием и передачу данных в физический порт, и модуля терминальной дисциплины ldterm(7m), который и отвечает за специфически терминальные функции и обработку терминальных команд ioctl(2).
При переходе к графическим дисплеям, возник вопрос, как обеспечить совместимость с программами, ориентированными на работу с терминалом. Для этого в ядре Unix предусмотрен интерфейс для создания специальных псевдоустройств, которые так и называются псевдотерминалами. Псевдоустройство или виртуальное устройство — это виртуальный объект, обслуживаемый специальным драйвером. Такой драйвер поддерживает такие же программные интерфейсы и структуры данных (минорную запись, специальный файл в каталоге /dev), как и обычный драйвер, но, в отличие от обычного драйвера, драйвер псевдоустройства не связан ни с каким физическим устройством, а имитирует все функции устройства программно. Примерами псевдоустройств являются файлы /dev/null, /dev/zero, /dev/random (в Linux). Псевдотерминалы создаются для поддержки сессий удаленного доступа через telnet(1), rlogin/rsh(1) и ssh(1), а также для терминальных сессий xterm(1) и gnome-terminal(1).
Псевдотерминал состоит из двух псевдоустройств, ведущего (/dev/pts/XX) и ведомого (/dev/pty/XX, где X — десятичная цифра). Процедура создания и открытия этих устройств различается в разных Unix-системах и подробно не рассматривается в этом курсе. Соответствующая процедура для Solaris описана на странице руководства pts(7D). Оба устройства, в действительности, обслуживаются одним и тем же модулем ядра и представляют собой нечто вроде трубы: данные, записываемые в дескриптор ведущего устройства, читаются из ведомого, и наоборот. Кроме того, ведомое устройство поддерживает все команды ioctl(2), обязательные для терминала, и все терминальные функции, перечисленные на предыдущей странице.
Рассмотрим использование псевдотерминала терминальным эмулятором xterm(1). xterm(1) представляет собой графическое приложение, использующее протокол X Window. При запуске, xterm(1) создает и открывает окно на локальном или удаленном дисплее, имя которого определяется переменной среды DISPLAY. Это окно представляет собой текстовое окно, по умолчанию имеющее размер 80x25 символов, что соответствует стандартному размеру экрана большинства видеотерминалов. Кроме того, xterm(1) создает пару ведущего и ведомого устройств псевдотерминала, запускает подпроцесс, в этом подпроцессе создает сессию вызовом setsid(2), открывает ведомое устройство на дескрипторы 0, 1 и 2 (при этом, соответствующий псевдотерминал становится управляющим терминалом этой сессии), устанавливает переменную среды TERM=xterm и запускает в этом подпроцессе программу. По умолчанию, имя программы берется из переменной среды SHELL, но, если указать соответствующие параметры xterm(1), можно запустить произвольную программу.
Затем, xterm(1) преобразует нажимаемые пользователем клавиши в коды ASCII или текущей национальной кодировки (в наше время, обычно, UTF-8) или, если это необходимо, в коды расширения, и передает эти символы в ведущее устройство, так что они поступают на стандартный ввод запущенной программы или какого-то из её подпроцессов.. Кроме того, данные, выводимые запущенной программой в дескрипторы 1 и 2, считываются процессом xterm(1) из ведущего устройства и отображаются в текстовом окне так же, как они отображались бы на дисплее видеотерминала. При этом, передаваемые программой коды расширения интерпретируются программой xterm(1) как команды перемещения курсора, изменения цвета текста и т. д., поэтому программы, рассчитанные на работу с видеотерминалом, работают в привычной для них среде.