Как выполнить чтение/запись на устройстве tty*?

Как выполнить чтение/запись на устройстве tty*?

У меня есть устройство, которое отправляет информацию через USB на мой компьютер. Arch Linux настраивает это устройство, создавая файл с именем ttyUSB0в /dev/. Я использовал GTKtermдля получения этой входящей информации и отображения ее в эмулируемом окне терминала.

Мой вопрос в том, как именно происходит GTKtermчтение/запись в этот ttyUSB0файл, и где я могу начать изучать, как реализовать подобную функциональность? То есть, в самой простой форме, как я могу записать символ в ttyUSB0, или, напротив, получить байт и записать его в файл?

решение1

TTY — это файлы, которые вы можете использовать так же, как и любые другие. Вы можете открывать их стандартными инструментами открытия файлов вашего языка и читать или писать из них. У них есть особое поведение, которое отличается от «обычных» файлов, но основы те же. Я расскажу о некоторых особых случаях в конце, но сначала эксперимент.

Одна интересная вещь, которую вы можете сделать прямо из обычного терминала. Запустите ttyи он выведет строку типа:

/dev/pts/2

Это устройство TTY, на котором работает ваш терминал. Вы можете что-нибудь написать на этом терминале:

$ echo Hello > /dev/pts/2
Hello
$

Вы даже можете прочитать оттуда:

$ read X < /dev/pts/2
hello
$ echo $X
hello
$

( read X— это команда sh «считывать строку из стандартного ввода в переменную X»; < — использовать /dev/pts/2 в качестве стандартного ввода для команды чтения; первое «hello» я набрал, а второе было распечатано).

Если вы откроете другую оболочку, скажем, с помощью screenили xterm, вы можете запустить run echo spooky > /dev/pts/2в этой оболочке, чтобы текст появился на вашем исходном терминале, и то же самое для других команд. Все это просто ваша оболочка открывает файл, не зная, что это TTY.


Вот очень простая программа на языке C, которая делает именно то, что вы просили: записывает один символ в /dev/pts/3, а затем считывает из него один байт:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>    
int main() {
    char byte;
    int fd = open("/dev/pts/3", O_RDWR);
    write(fd, "X", 1);
    ssize_t size = read(fd, &byte, 1);
    printf("Read byte %c\n", byte);
    return 0;
}

Настоящее устройство TTY, подключенное к оболочке или эмулятору терминала, будет вести себя интересно, но вы должны что-то получить взамен.


Чтобы получить доступ к терминалу, вам необходимо разрешение на его использование. Это просто стандартные разрешения для файлов, которые вы видите ls -lи устанавливаете с помощью chmod: вам необходимо разрешение на чтение, чтобы открыть файл и прочитать его, и разрешение на запись, чтобы записать в него. TTY, которые поддерживают ваш терминал, будут принадлежать вам, но TTY другого пользователя не будут, а TTY для USB-устройств могут принадлежать вам или нет, в зависимости от вашей конфигурации. Вы можете изменить разрешения так же, как и всегда.

Что касается написания программы для работы с ним, вам не нужно делать ничего особенного. Вы можете видеть в примере, что одна вещь, которую выненужно сделать, это закрыть файл каждый раз, чтобы ваши данные были прочитаны на другом конце: файлы TTY действуют как конвейеры, просто передавая данные в обоих направлениях по мере их поступления. Когда я писал текст в TTY, он появлялся немедленно, а когда я читал его впоследствии, меня уже ничего не ждало. Этонеткак запись в обычный файл, когда данные сохраняются на диске — они немедленно передаются на другую сторону или хранятся в памяти до тех пор, пока кто-то их не прочитает.

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

Следует помнить, что в ядре может быть ограничен размер буфера, и если вы пишете много данных за раз, вы можете неожиданно заблокироваться. Если это может стать проблемой, используйтенеблокирующий ввод-выводс open("/dev/...", O_RDWR | O_NONBLOCK). Принцип в любом случае будет один и тот же.

Связанный контент