
В Linux Терминал связан с Оболочкой. Терминал отправляет ввод в Оболочку (например: pwd
), а Оболочка отправляет вывод обратно в Терминал (например: /home/paul
).
На этой диаграмме показана связь между Терминалом и Оболочкой (предположим, что Терминал, который я использую, — это gnome-terminal
, а Оболочка — это bash
):
Теперь я хочу узнать, какой механизм используют Терминал и Shell для обмена данными. Вот что, по-моему, происходит:
- При
gnome-terminal
выполнении он создаст файл, представляющий последовательный порт в каталоге/dev/pts
(допустим, имя файла —/dev/pts/0
). gnome-terminal
затем запустит связанную с ней оболочку (например:bash
) и передаст ей имя файла pts (имя файла pts можно передать, например, через аргументы командной строки).- Теперь и то
gnome-terminal
, иbash
другое начнет читать с/dev/pts/0
. - При
gnome-terminal
необходимости отправки данных вbash
, он записывает эти данные в/dev/pts/0
, а затемbash
считывает эти данные из/dev/pts/0
. - Когда
bash
требуется отправить данные вgnome-terminal
, он записывает эти данные в/dev/pts/0
,and
gnome-terminal
считывает эти данные из/dev/pts/0
.
На этой диаграмме показано то, что я только что объяснил:
Правильно ли я понимаю?
Примечание: конечно, файл pts мог бы быть файлом tty, если бы мы использовали виртуальные терминалы (т.е. когда мы не используем графический интерфейс), но логика все равно была бы той же.
решение1
Вам не хватает необходимой части. Устройства псевдо-tty не симметричны, как сокеты. Есть главный и подчиненный конец. Файлы в /dev/pts
представляют подчиненные устройства.
Эмулятор терминала создает псевдо-tty путем вызова openpty
(или, forkpty
что openpty
плюс некоторая бонусная настройка для общего случая, когда вы хотите запустить новый процесс на вашем новом tty). На более низком уровне это включает открытие /dev/ptmx
и выполнение некоторых магических ioctl.
В результате вызова openpty
эмулятор терминала получает пару файловых дескрипторов, а также может получить имя файла в /dev/pts
соответствии с подчиненным процессом. Главный процесс не получает индивидуального имени, поскольку дочернему процессу никогда не нужно открывать его по имени.
Ведущие и ведомые устройства действуют как противоположные концы сокета: то, что вы пишете на одном конце, считывается с другого. Но поскольку это tty, все режимы tty применяются к данным на пути.
Например, если вы эмулятор терминала и получаете нажатие клавиши A, вы должны записать 'a'
в главный файловый дескриптор. Это напрямую эквивалентно отправке этого байта по последовательной линии с терминала на компьютер. Это приведет к 'a'
считыванию с подчиненного устройства (любой программой, которая его считывает, например, оболочкой).
Если вы получили нажатие клавиши, Dкогда Ctrlклавиша нажата, вы должны записать 4
байт ( 'D' ^ 0x40
) в главный файловый дескриптор. (Потому что это то, что настоящий терминал отправляет по проводу.) Что произойдет дальше, зависит от режима tty. В необработанном режиме программа, считывающая подчиненный tty, увидит байт 4
. В приготовленном режиме tty активирует поведение «нажата специальная клавиша EOF».
В обратном направлении также есть некоторая обработка. Когда какая-то программа пишет '\n'
в подчиненный tty, вы, вероятно, получите "\r\n"
на главном файловом дескрипторе, из-за onlcr
постобработки.
Раздел «История», пропустите, если скучно
Давным-давно у подчиненных устройств были имена вроде , /dev/ttyp0
и у каждого из них был соответствующий мастер, вроде /dev/ptyp0
. Они не создавались динамически. Эмулятор терминала мог просто проверить их все, чтобы найти неиспользуемое в данный момент устройство, и начать его использовать. Управление владением и разрешениями было проблемой. xterm
setuid-root был просто для того, чтобы он мог chownить подчиненное устройство.
Новая схема, называемая «UNIX98 ptys», управляет созданием устройств и владением ими с помощью магических ioctl, поэтому файлы появляются только тогда, /dev/pts
когда они используются, и ими владеет пользователь, запустивший программу, создавшую их.