Intereting Posts
Можно ли поставлять пакеты Guix другим дистрибутивам? Что означает эта ошибка python2 и что мне делать с этим? syncookies и tcp options Не удалось загрузить на Debian после клонирования двойной загрузки с HDD на SSD использовать переменную экспорта как часть имени файла в awk-программе Изменение постоянной таблицы маршрутизации эквивалент package.use для VIDEO_CARDS, ABI_X86 и т. д. Быстрый способ скопировать большой файл в локальной сети как запустить пользовательский скрипт после пробуждения systemd? Как написать скрипт для запуска, только если другой скрипт запущен совсем недавно? logrotation – вращение и команда maxage Подготовить строки с префиксом файла с помощью sed или awk Какой вариант ls следует использовать для понимания изменений метаданных? Использование pinentry с keepass2 для шифрования почты gpg IPv6 настраиваемый кольцевой адрес и пинг его с другого хоста

Как каналы ввода / вывода реализованы в ядре Linux?

stdin, stdout, stderr – это целые числа, которые индексируются в структуру данных, которая «знает», какие каналы ввода / вывода должны использоваться для процесса. Я понимаю, что эта структура данных уникальна для каждого процесса. Являются ли каналы ввода-вывода ничем иным, как структурами массива данных с распределением динамической памяти?

В Unix-подобных операционных системах стандартные потоки ввода, вывода и ошибок идентифицируются файловыми дескрипторами 0 , 1 , 2 . В Linux они видны под файловой системой proc в /proc/[pid]/fs/{0,1,2} . Эти файлы на самом деле являются символическими ссылками на псевдотерминальное устройство в каталоге /dev/pts .

Псевдотерминал (PTY) представляет собой пару виртуальных устройств, псевдотерминальный мастер (ПТМ) и псевдотерминальный подчиненный (PTS) (совместно именуемый как псевдотерминальная пара ), которые обеспечивают канал IPC, несколько похожий на двунаправленный канал между программой, которая ожидает для подключения к терминальному устройству и программу драйвера, которая использует псевдотерминал для отправки ввода и приема ввода от предыдущей программы.

Ключевым моментом является то, что псевдотерминал-ведомый появляется как обычный терминал, например, он может переключаться между неканоническим и каноническим режимами (по умолчанию), в котором он интерпретирует определенные входные символы, такие как генерирование сигнала SIGINT когда символ прерывания (обычно сгенерированный нажатием Ctrl + C на клавиатуре) записывается в псевдотерминальный мастер или заставляет следующее read() возвращать 0 когда встречается символ конца файла (обычно генерируемый Ctrl + D ). Другие операции, поддерживаемые терминалами, включаются или выключаются эхом, устанавливая группу процессов переднего плана и т. Д.

Псевдотерминалы имеют ряд применений:

  • Они позволяют программам типа ssh управлять терминальными программами на другом узле, подключенном через сеть. Программа, ориентированная на терминал, может быть любой программой, которая обычно запускается в сеансе интерактивного терминала. Стандартный ввод, вывод и ошибка такой программы не могут быть подключены непосредственно к сокету, поскольку сокеты не поддерживают вышеупомянутую функциональность терминала.

  • Они позволяют программам, например, expect от интерактивной программы, ориентированной на терминал, из сценария.

  • Они используются терминальными эмуляторами, такими как xterm для обеспечения функциональности терминала.

  • Они используются такими программами, как screen для мультиплексирования одного физического терминала между несколькими процессами.

  • Они используются программами, такими как script для записи всех входных и выходных данных во время сеанса оболочки.

Unix98-стиль PTY , используемый в Linux, настраивается следующим образом:

  • Программа драйвера открывает мастер-мультиплексор псевдотерминала в dev/ptmx , на котором он получает дескриптор файла aa для PTM, а устройство PTS создается в каталоге /dev/pts . Каждый файловый дескриптор, полученный путем открытия /dev/ptmx является независимым PTM со своим собственным PTS.

  • Программы-драйверы вызывает fork() для создания дочернего процесса, который, в свою очередь, выполняет следующие шаги:

    • Ребенок вызывает setsid() чтобы начать новый сеанс, из которых ребенок является лидером сеанса. Это также заставляет ребенка потерять контрольный терминал .

    • Ребенок переходит к открытию устройства PTS, которое соответствует PTM, созданного программой драйвера. Поскольку ребенок является лидером сеанса, но не имеет управляющего терминала, PTS становится дочерним управляющим терминалом.

    • Ребенок использует dup() для дублирования файлового дескриптора для подчиненного устройства на нем стандартный ввод, вывод и ошибку.

    • Наконец, ребенок вызывает exec() для запуска терминальной программы, которая должна быть подключена к псевдотерминальному устройству.

На этом этапе все, что программа драйвера записывает в PTM, появляется как вход в программу, ориентированную на терминал, на PTS, и наоборот.

При работе в каноническом режиме вход в PTS буферизуется по строкам. Другими словами, как и при использовании обычных терминалов, чтение программы из PTS получает строку ввода только тогда, когда символ новой строки записывается в PTM. Когда емкость буферизации исчерпана, дальнейшая write() вызывает блок до тех пор, пока часть входа не будет потреблена.

В ядре Linux системные вызовы open() , read() , write() stat() и т. Д. Реализованы на уровне виртуальной файловой системы (VFS), который обеспечивает единый интерфейс файловой системы для программ пользовательского пространства. VFS позволяет различным реализациям файловой системы сосуществовать внутри ядра. Когда пользовательские программы вызывают вышеупомянутые системные вызовы, VFS перенаправляет вызов на соответствующую реализацию файловой системы.

Устройства PTS под /dev/pts управляются devpts файловой системы /fs/devpts/inode.c , определенной в /fs/devpts/inode.c , тогда как драйвер TTY, обеспечивающий устройство ptmx в стиле ptmx определяется в drivers/tty/pty.c ,

Буферизация между устройствами TTY и дисциплинами линий TTY, такими как псевдотерминалы, предоставляется структура буфера, поддерживаемая для каждого устройства tty, определенное в include/linux/tty.h

До версии ядра 3.7 буфер был флип-буфером :

 #define TTY_FLIPBUF_SIZE 512 struct tty_flip_buffer { struct tq_struct tqueue; struct semaphore pty_sem; char *char_buf_ptr; unsigned char *flag_buf_ptr; int count; int buf_num; unsigned char char_buf[2*TTY_FLIPBUF_SIZE]; char flag_buf[2*TTY_FLIPBUF_SIZE]; unsigned char slop[4]; }; 

Структура содержала хранилище, разделенное на два буфера равного размера. Буферы были пронумерованы 0 (первая половина char_buf/flag_buf ) и 1 (вторая половина). Драйвер хранит данные в буфер, идентифицированный buf_num . Другой буфер может быть сброшен в линейную дисциплину.

Буфер был «перевернут», переключив buf_num между 0 и 1 . Когда buf_num изменен, char_buf_ptr и flag_buf_ptr были установлены в начало буфера, идентифицированного buf_num , и count был установлен в 0 .

Начиная с версии ядра 3.7, флип-буферы TTY были заменены объектами, выделенными через kmalloc() организованными кольцами . В нормальной ситуации для последовательного порта, управляемого IRQ, на типичных скоростях их поведение почти такое же, как со старым флип-буфером; два буфера в конечном итоге распределяются, а циклы ядра между ними, как и раньше. Однако, когда есть задержки или скорость увеличивается, новая реализация буфера работает лучше, поскольку пул буферов может немного расти.

Из man-страниц для любого из трех объясняется ответ:

  Under normal circumstances every UNIX program has three streams opened for it when it starts up, one for input, one for output, and one for printing diagnostic or error messages. These are typically attached to the user's terminal but might instead refer to files or other devices, depending on what the parent process chose to set up. The input stream is referred to as "standard input"; the output stream is referred to as "standard output"; and the error stream is referred to as "standard error". These terms are abbreviated to form the sym- bols used to refer to these files, namely stdin, stdout, and stderr. Each of these symbols is a stdio(3) macro of type pointer to FILE, and can be used with functions like fprintf(3) or fread(3). Since FILEs are a buffering wrapper around UNIX file descriptors, the same underlying files may also be accessed using the raw UNIX file interface, that is, the functions like read(2) and lseek(2). On program startup, the integer file descriptors associated with the streams stdin, stdout, and stderr are 0, 1, and 2, respectively. The preprocessor symbols STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are defined with these values in <unistd.h>.