Устройства tty с программным обеспечением на обоих концах называются псевдотерминалами (pseudo-tty, или же просто pty). В первой части главы они рассматриваться не будут, поскольку программный конец pty обрабатывается так же, как и любое другое устройство tty. Позже мы поговорим о программировании аппаратного конца pty.
16.1. Операции tty
Устройства tty предоставляют огромное количество опций обработки данных; они относятся к наиболее сложным устройствам ядра. Настраивать можно опции обработки входных и выходных данных, а также потока данных. Также можно контролировать ограниченное манипулирование данными на уровне драйвера устройства.
Интерфейсы tty работают в двух основных режимах: неформатируемый режим и режим обработки. Неформатируемый режим передает данные в приложение без изменений. Режим обработки, известный также как канонический режим, поддерживает ограниченный построчный редактор внутри драйвера устройства и пересылает отредактированные входные данные в приложение по одной строке за раз. Этот режим изначально произрастает из универсальных систем, в которых специализированные устройства обработки входных данных предоставляли режим обработки без прерывания ЦП.
Режим обработки обрабатывает определенные управляющие символы; например, по умолчанию ^U уничтожает (стирает) текущую строку, ^W стирает текущее слово, забой (^Н) или Delete — предыдущий символ, a ^R стирает и затем повторно набирает текущую строку. Каждое из этих управляющих действий может быть повторно назначено другом символу. Например, на многих терминалах символу DEL (код 127) назначается действие забоя.
16.1.1. Служебные функции терминалов
Иногда невозможно узнать, соответствует ли файловый дескриптор tty. Чаще всего это связано со стандартным дескриптором выходного файла. Программы, выводящие текст на стандартные устройства, часто форматируются иначе при записи в канал, чем при отображении информации для пользователей. Например, в случае применения команды ls
для получения списка файлов, она отобразит несколько колонок при простом запуске (удобнее читать человеку), но когда вы перенаправите ее вывод в другую программу, она отобразит по одному файлу в строке (удобнее читать программе). Запустите ls
и ls | cat
и почувствуйте разницу.
Определить, соответствует ли файловый дескриптор tty, можно с помощью функции isatty()
, принимающей файловый дескриптор в качестве своего аргумента и возвращающей 1, если дескриптор соответствует tty, и 0 в противном случае.
#include
int isatty(int fd);
Функция ttyname()
предоставляет каноническое имя для терминала, ассоциированное с файловым дескриптором. В качестве аргумента она принимает любой файловый дескриптор, возвращая указатель в поток символов.
#include
char *ttyname(int fd);
Поскольку поток символов расположен в статическом пространстве, потребуется сделать копию возвращенной строки перед повторным вызовом ttyname()
; эта функция не реентерабельна. ttyname()
возвращает NULL
при любой ошибке, включая случай передачи файлового дескриптора, не ассоциированного с tty.
16.1.2. Управляющие терминалы
Каждый сеанс (см. главу 10) привязан к терминалу, с которого процессы сеанса получают свои входные данные и в который пересылают свои выходные данные. Этот терминал может быть локальной консолью машины, терминалом, подключенным через последовательный канал, либо псевдотерминалом, устанавливающим соответствия во внешнем окне или по всей сети (подробнее о псевдотерминалах читайте в конце этой главы). Терминал, к которому относится сеанс, называется управляющим терминалом (или управляющим tty) сеанса. Терминал может быть управляющим терминалом только в одном сеансе за раз.
Нормальные процессы не могут менять свои управляющие терминалы; это может делать только лидер сеанса. В Linux изменение управляющего терминала лидера сеанса не распространяется на другие процессы того же сеанса. Лидеры сеансов почти всегда устанавливают управляющий терминал при запуске до создания каких-либо дочерних процессов, чтобы гарантировать, что все процессы сеанса совместно используют один управляющий терминал.
Существуют два интерфейса для смены управляющего tty лидера сеанса. Первый реализуется с помощью нормальных системных вызовов open()
и close()
.
1. Закройте все файловые дескрипторы, относящиеся к текущему управляющему терминалу.
2. Откройте новый терминал без установки флага O_NOCTTY
.
Второй метод включает вызовы ioctl()
на отдельных файловых дескрипторах, ссылающихся на старые и новые терминалы.
1. Установите флаг TIOCNOTTY
на файловый дескриптор, привязанный к исходному управляющему tty (обычно ioctl(0, TIOCNOTTY, NULL)
нормально работает). Это разрывает соединение между сеансом и tty.