Что делает tty, когда процесс запрашивает ввод или вывод на экран?

Я прочитал множество сообщений о tty. Все они начинаются с исторических причин названия tty. Пожалуйста, оставьте это и просто опишите систему tty, как она существует сегодня. Затем они говорят о том, что tty — это файл, и что stdin, stdout и sterr процесса, запущенного в терминале, сопоставляются с этим файлом. Как три файла сопоставляются с одним файлом?

Некоторые говорят, что tty позволяет редактировать строку до того, как будет нажата клавиша ввода, и выполняет другие действия по дисциплине строки. Существует сообщение в блоге, в котором говорится, что у каждого tty есть собственный стандартный ввод и стандартный вывод. В сообщении в блоге Линуса Акессона, с которым я все еще не могу разобраться, объясняется, что на самом деле это драйвер tty в ядре и файл устройства tty. затем есть управляющий терминал, сеансы, эмуляторы терминала, необработанные и приготовленные режимы, pty и многое другое.

Чтобы лучше понять, что такое tty, может кто-нибудь объяснить мне, что происходит в этой простой ситуации: открывается терминал, и он запускает оболочку по умолчанию. Из оболочки запускается процесс и запрашивает ввод.

  • Что происходит, когда выполняется вызов scanf?
  • Как терминал узнает, что вызывается scanf?
  • Буфер редактирования в терминале, который мы видим потом (строка, куда мы вводим текст) - откуда он? Существует ли этот буфер в файле устройства tty и выводится ли он так же, как печатается файл stdout?
  • Какой процесс управляет этим буфером? Драйвер телетайпа?
  • Что происходит, когда мы нажимаем ввод? Отправляет ли драйвер tty строку в часть stdin устройства tty?
  • Как процесс узнает, что ввод был отправлен.

И выходная часть: когда тот же процесс что-то выводит, пишет ли он на tty-устройство? Но разве tty уже не выводит текущую строку буфера редактирования?

ЕСЛИ есть лучший способ описать, что делает tty, не отвечая на приведенные выше вопросы, пожалуйста, сделайте это. Если я пропустил какую-то важную часть, пожалуйста, заполните, как вы считаете нужным.


person uniqueid    schedule 13.08.2020    source источник


Ответы (1)


Это получилось очень долго, так что приготовьтесь...

Устройство телетайпа

Вы смотрите на TTY как на экран, разделенный, например, на Плитка 80х24. Но TTY — это консоль: она содержит устройство ввода (обычно подключаемое к клавиатуре) и устройство вывода (обычно подключаемое к экрану).

Абстракция телетайпа

Пока TTY подключен к (физическим или смоделированным) устройствам, процессы Unix не видят устройства, они видят абстракцию. Эта абстракция состоит из входного потока, выходного потока и интерфейса управления.

Интерфейс управления может включать/отключать некоторые причудливые функции, такие как отправка входных данных, которые он получает, не только процессу, использующему терминал, но и своему собственному выходному потоку (эта функция называется эхом и может управляться с помощью stty echo), но факт то, что вы видите что-то на выходе терминала, не означает, что оно подключено к какой-либо форме stdout.

Эта абстракция реализована в ядре драйвером TTY и линейной дисциплиной. Вы можете думать о линейной дисциплине как о шаблоне проектирования Стратегии.

Эта абстракция должна быть доступна для пользовательского пространства, и способ, которым драйверы Unix экспортируют что-либо в пользовательское пространство, заключается в создании специальных файлов, таких как /dev/tty.

Файл TTY позволяет вам read() вводить поток, write() в выходной поток и включать/выключать функции с помощью ioctl()

TTY-файлы

Обычно каждый раз, когда вы запускаете новый терминал, драйвер создает новый файл TTY.

Любой процесс может открыть tty-файл, независимо от того, будет ли этот файл стандартом stdin, stdout, stderr, всеми ими или ни одним из них.

Вы можете убедиться сами: откройте терминал и введите tty. Допустим, он напечатал /dev/pts/3.

Теперь откройте другой терминал и запустите:

exec 10>/dev/pts/3 # open /dev/pts/3 as file descriptor 10
echo hello >&10 # write "hello" through file descriptor 10

Это заставит echo записать hello в файловый дескриптор 10, который является первым терминалом. Соответственно первый терминал напечатает hello.

Стандартные потоки

Unix реализует 3 стандартных потока: stdin, stdout и stderr. Они не получают никакой специальной обработки со стороны драйвера терминала или линейной дисциплины, и большая часть их реализации находится в оболочке.

Когда вы запускаете эмулятор терминала, он открывает tty-файл, скажем, /dev/pts/3. Затем он создает новый процесс (fork()), открывает /dev/pts/3 как файловые дескрипторы 0 (stdin), 1 (stdout) и 2 (stderr), а затем запускает оболочку.

Это означает, что когда оболочка запускается, она имеет файл терминала с его потоками и интерфейсами управления. Когда оболочка записывает либо на стандартный ввод, либо на стандартный вывод, обе записи передаются в выходной поток TTY.

Когда оболочка выполняет другой процесс, этот процесс наследует /dev/pts/3 в качестве своих файловых дескрипторов 0, 1, 2, если только оболочка не выполняет перенаправление или исполняемые программы не изменяют эти файловые дескрипторы.

Конкретные ответы

Теперь мы готовы ответить на ваши вопросы:

Что происходит, когда выполняется вызов scanf?

scanf() вызывает read(STDIN), который вызывает реализацию драйвера TTY read().

В приготовленном режиме это будет блокироваться до тех пор, пока входной поток не буферизует полную строку. В необработанном режиме он будет блокироваться до тех пор, пока не будет прочитан хотя бы один символ.

Затем входной буфер TTY копируется в буфер scanf.

Как терминал узнает, что вызывается scanf?

Это не так. Если вы введете что-то в терминал во время работы программы и не ожидаете ввода, это будет буферизовано во входном потоке терминала.

Затем всякий раз, когда вызывается scanf, если вообще вызывается, он будет считывать этот буфер. Если scanf не вызывается, то программа завершается, управление возвращается оболочке. и оболочка читает этот буфер. Вы можете увидеть это, запустив sleep 30, и пока он работает, введите другую команду и нажмите Enter. Оболочка выполнит его после выполнения sleep:

bash-4.3$ sleep 30
echo hello
bash-4.3$ echo hello
hello
bash-4.3$ 

Буфер редактирования в терминале, который мы видим потом (строка, куда мы вводим текст) - откуда он? Существует ли этот буфер в файле устройства tty и выводится ли он так же, как печатается файл stdout?

Буфер существует в ядре и прикреплен к файлу TTY.

Если функция эха терминала включена, линейная дисциплина будет отправлять входной поток не только во входной буфер, но и в выходной поток.

Если терминал находится в режиме приготовления (по умолчанию), линейная дисциплина будет давать специальные символы обработки, такие как backspace (да, backspace — это символ, ASCII 8). В случае backspace удалит последний символ из входного буфера, а в выходной поток отправит управляющую последовательность для удаления последнего символа с экрана.

Обратите внимание, что буфер управляется отдельно от того, что вы видите на экране.

Какой процесс управляет этим буфером? Драйвер телетайпа?

Буфер находится в ядре и управляется не процессом, а линейной дисциплиной, которая контролируется драйвером TTY.

Что происходит, когда мы нажимаем ввод? Отправляет ли драйвер tty строку в часть stdin устройства tty?

Когда вы нажимаете ввод, в буфер добавляется символ перевода строки (\n), и, если какой-либо процесс ожидает ввода терминалом, входной буфер копируется в буфер процесса, а процесс разблокируется и продолжает работать.

Более интересный вопрос заключается в том, что происходит, когда вы нажимаете что-то, что НЕ является вводом. В необработанном режиме не имеет значения, введен он или нет, потому что \n не получает никакой специальной обработки. Однако в подготовленном режиме входной буфер не копируется и процесс чтения не уведомляется.

Как процесс узнает, что ввод был отправлен.

Процесс вызывает, например. scanf() который read(STDIN) будет блокировать процесс до тех пор, пока ввод не будет доступен. Когда ввод доступен, драйвер TTY разблокирует заблокированный процесс (т. е. разбудит его).

Обратите внимание, что это не относится к файлам TTY или STDIN, это относится ко всем файлам, именно так работает read().

Также обратите внимание, что scanf() не знает, является ли STDIN файлом TTY.

Когда тот же процесс что-то выводит, пишет ли он на tty-устройство?

когда вы вызываете что-то вроде printf(), он вызывает write(STDOUT), который вызывает реализацию драйвера TTY write(), которая записывает в выходной поток TTY.

Опять же, обратите внимание, что printf() не знает, является ли STDOUT файлом TTY.

Но разве tty уже не выводит текущую строку буфера редактирования?

В Unix файл (любой файл, а не только файлы TTY) может быть открыт и записан несколькими авторами, и синхронизация между ними не гарантируется.

Как видно из приведенного выше примера echo hello >&10, процесс, работающий в терминале, — это не единственное, что может записывать в поток вывода TTY, но даже несвязанный процесс может записывать в поток вывода TTY.

А когда эхо включено, линейная дисциплина также может записывать в выходной поток TTY.

Все эти записи будут чередоваться, драйвер не будет пытаться их синхронизировать или понять.

person root    schedule 16.08.2020