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

Вы не должны знать количество строк заранее. tail и head может взять смещение с начала или конца файла соответственно.

Этот канал запускается во второй строке файла (пропускающий первую строку) и останавливается в предпоследнем (пропуск заключительной строки). Чтобы пропустить больше чем одну строку вначале или закончиться, скорректируйте числа соответственно.

tail -n +2 file.txt | head -n -1

при выполнении его наоборот, работает то же, конечно:

head -n -1 file.txt | tail -n +2
8
20.10.2013, 02:30
2 ответа

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

Псевдотерминал (ИМУЩЕСТВО) является парой виртуальных устройств, ведущее устройство псевдотерминала (PTM) и ведомое устройство псевдотерминала (PTS) (коллективно упомянул s пара псевдотерминала), которые обеспечивают канал IPC, несколько как двунаправленный канал между программой, которая ожидает быть подключенной к оконечному устройству и программе драйвера, которая использует псевдотерминал для отправки входа в, и получают вход от бывшей программы.

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

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

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

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

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

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

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

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

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

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

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

    • Ребенок продолжает открывать устройство PTS, которое соответствует PTM, созданному программой драйвера. Так как ребенок является лидером сессии, но не имеет никакого терминала управления, PTS становится терминалом управления childs.

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

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

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

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

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

Устройства PTS под/dev/pts управляются devpts файловая система implemention определенный в /fs/devpts/inode.c, в то время как драйвер TTY, обеспечивающий Unix98-стиль 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 управляемый последовательный порт на типичных скоростях их поведение является в значительной степени тем же как со старым зеркально отраженным буфером; два буфера заканчиваются выделенные и циклы ядра между ними как прежде. Однако, когда существуют задержки или увеличения скорости, новая буферная реализация работает лучше, поскольку пул буферов может вырасти немного.

14
27.01.2020, 20:10

Из страниц справочника для любого из трех это объясняет ответ:

   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>.
-1
27.01.2020, 20:10
  • 1
    Этот ответ описывает реализацию stdin, stdout и stderr с точки зрения библиотеки C, но вопроса явно о реализации ядра. Я пытался объяснить точку зрения ядра в своем ответе. –  Thomas Nyman 27.11.2013, 16:12

Теги

Похожие вопросы