Letysite.ru

IT Новости с интернет пространства
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Именованные каналы linux

Pipes: Программные каналы в Linux

Содержание

Предисловие

В одной остроумной статье я прочел следующую сентенцию: «Лишить приверженца Юникс программных каналов — то же самое, что отобрать мышь у пользователя Виндоуз». Возможно, в этом утверждении и есть некоторое преувеличение, но в прежние времена так оно, по большому счету, и было. Опытные сторонники Юниксовидных систем любят консоль и умеют ею пользоваться. Мы же, нынешние, установив Убунту, уже считаем себя линуксоидами, а что такое консоль, имеем смутное представления. Но проходит некоторое время, и, устав от украшения рабочего стола, прочитав две-три статейки, мы решаемся нажать мышкой на значок монитора в системном трее. Со временем перед нами открывается новый мир, полный удивительных возможностей и беспрерывного совершенствования своих знаний, мир пиршества интеллекта, непрекращающегося эксперимента, и радости оттого, что ты Homo Sapiens. Девиз: «Вернем радость в общение с компьютером!», — как нельзя лучше подходит для этого случая.

Предлагаемая вашему вниманию статья как раз для тех, кто недавно открыл для себя командную строку Линукс.

Выбор термина

Термин pipe (труба) чрезвычайно органично вошел в англоязычный компьютерный жаргон. Этим словом называется не только способ передачи вывода одной команды на ввод другой, но и оператор, которым обозначается это действие: | (вертикальная черта). Кроме того, то же слово служит глаголом, означающим данное действие.

Какие только термины не используют в русском языке для перевода слова «pipes»: и трубы, и трубопроводы, и конвейеры, и потоки, и прочее. В контексте все эти термины выглядят довольно неуклюже. И вот еще беда — ни от одного из этих существительных нельзя образовать глагол, не говоря уже о том, чтобы называть так символ вертикальной черты. Можно, правда, употребить глагол «конвейеризировать», но такое не написать, не выговорить невозможно. Я пытался делать наметки этой статьи, используя все перечисленные термины, но не был удовлетворен ни одним.

Совершенно случайно, в книге А. Робачевского «Операционная система UNIX» мне встретился термин «программные каналы». Поначалу он показался мне несколько громоздким, но попробовав его на деле, я убедился в его несомненных преимуществах. Он не выглядит смешно и дико как «трубы», от него легко произвести глагол, и, самое главное, он имеет вполне прижившегося на русской почве брата — «именованные каналы», которые никто не назовет «именованными трубопроводами». Итак, решено, в данной статье термин pipes будет звучать как «программные каналы».

Введение в программные каналы

Программным каналом называется использование вывода одной команды в качестве ввода для другой программы. Например:

Команда dmesg выводит сообщения ядра Линукс о процессе загрузки ОС (те самые, что пробегают по экрану монитора при загрузке системы). Эти сообщения не умещаются на одном экране, и пролетают так быстро, что прочесть их невозможно. Поэтому вывод программы dmesg передают на ввод команде less. (Команда less позволяет выводу команды dmesg заполнить только один экран. Чтобы прочесть следующую порцию текста, нужно нажать клавишу пробела, а чтобы вернуться к предыдущей порции — клавишу b. Прервать работу программы можно клавишей q). Оператором такой передачи служит вертикальная черта (|). (Пробелы до и после вертикальной черты ставятся для удобства чтения, но можно обойтись и без них). Все вместе и есть простейший программный канал.

Того же результата можно достичь, если сначала перенаправить вывод команды dmesg во временный файл, а затем просмотреть содержимое этого файла на экране монитора.

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

Необходимо пояснить понятия, которые я походя назвал «вводом» и «выводом» программы.

Любая программа командной оболочки (шелла) оперирует с тремя потоками данных: стандартным вводом (stdin), стандартным выводом (stdout), и стандартным сообщением об ошибке (stderr). (Подробно об этом можно прочесть в статье «Перенаправление стандартных потоков данных»).

По умолчанию, стандартный ввод осуществляется с клавиатуры, а стандартный вывод — на экран монитора. Если же мы задействуем оператор программных каналов (|), то стандартный вывод первой программы станет стандартным вводом второй, при этом на экране монитора он уже не появится.

Такая цепочка вовсе не ограничивается двумя программами, но может продолжаться сколь угодно долго.

Как это работает

В большинстве Юниксовидных систем все процессы в программном канале начинаются одновременно, их потоки соответственно соединяются и управляются планировщиком вместе со всеми остальными процессами, идущими в системе.

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

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

Как пользоваться программными каналами

Чаще всего употребляются программные каналы, заканчивающиеся командами less и more. Эти две команды схожи по своему действию, однако less новее и имеет ряд дополнительных функций, включая возможность вернуться к предыдущим «страницам» вывода. Многие пользуются этими программными каналами, не подозревая, что занимаются столь мудреными вещами.

Кроме вышеприведенного примера с каналом dmesg | less, часто используется канал ls | less. Команда ls позволяет просматривать содержимое директорий, а с опцией -l дает подробные сведения о файлах, «населяющих» указанную директорию. Если директория содержит достаточно файлов, чтобы их список занял больше одного экрана, то применение программного канала с less или more неизбежно:

Для пробы проделайте такой пример:

Только запаситесь терпением — на моей небольшой системе, установленной с одного CD, в выводе было 87 187 строк, сиречь файлов. Дело в том, что опция -R команды ls выводит содержимое директории рекурсивно, то есть открывая подкаталоги, подкаталоги подкаталогов и так далее, пока не перечислит все файлы. Правда, чтобы просмотреть действительно все файлы в директории, нужно войти как администратор (root), потому что некоторые каталоги могут не давать прав доступа рядовому пользователю.

Понятно, что найти «вручную» что-либо в таком списке проблематично, и тут на помощь снова придут программные каналы.

Команда grep найдет нужные вам строки, если вы зададите образец для поиска:

Обратите внимание на символ # в начале командной строки — он означает, что я вошел с правами суперпользователя.

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

Среди команд-фильтров самая употребительная, без сомнения, grep. Она применяется везде, где нужно выбрать искомое из большого объема данных. Скажем, просмотреть все, что касается USB в выводе команды dmesg:

Это только начало списка строк, выведенных командой grep -i usb, я не привожу его полностью из экономии места. Опция -i приказывает команде grep не замечать разницы между заглавными и строчными буквами.

Любой системный администратор часто пользуется командой ps. С опциями -e и -f она выводит все процессы, текущие в системе в полной форме (подробно). Процессов этих весьма много, поэтому я не привожу полный вывод команды:

Чтобы найти в этом списке интересующие вас процессы, следует канализировать команду ps с командой grep. Допустим, вас интересуют процессы hald:

С таким коротким списком уже легче работать. (Обратите внимание на последнюю строчку, там представлен сам запущенный нами процесс grep hald).

Другие распространенные команды-фильтры

Кроме команды grep (или вместе с ней) часто употребляются следующие команды:

  • sort — сортирует строки по алфавиту или порядку номеров
  • wc — подсчитывает количество строк, слов, байт или символов в тексте
  • tr — заменяет одни символы другими
  • sed — позволяет редактировать текст прямо из командной строки, даже не видя его.
  • cut — вырезает из текста нужные куски и выдает их на стандартный вывод
  • head/tail — позволяют ограничить просмотр первыми несколькими строками (head — голова), либо последними несколькими строками (tail — хвост).

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

Сложные программные каналы

Вот пример, как наладить проверку орфографии, используя программные каналы. Допустим, что у вас есть файл words.txt, в котором содержатся все слова английского языка (разумеется, такого файла у вас нет, но можно позаимствовать список слов из какого-нибудь словаря; а английского — чтобы не путаться с кодировками). Тогда составляется следующий программный канал:

Читать еще:  Подключение к удаленному рабочему столу linux

Примечание: Символ () используется для объединения всех шести строк в одну командную строку.

Команда первая: wget получает содержимое HTML web страницы.

Команда вторая: sed удаляет из текста страницы все символы, не являющиеся пробелами или буквами и заменяет их пробелами.

Команда третья: tr переводит все символы верхнего регистра в нижний регистр (заглавные буквы в строчные), а также конвертирует пробелы в строках в символы новой строки, так что теперь каждое «слово» является новой строкой.

Команда четвертая: grep оставляет только строки, содержащие хотя бы один алфавитный символ (попросту букву), удаляя все пустые строки.

Команда пятая: sort сортирует список «слов» в алфавитном порядке, а с опцией -u удаляет дубликаты.

Команда шестая, и последняя: comm находит строки, общие для двух файлов. Первым файлом является стандартный вывод нашего программного канала, для чего вместо имени первого файла стоит прочерк (-), вторым файлом будет файл words.txt. Строки, которые встречаются только во втором файле и те, что встречаются в обоих файлах, подавляются опциями -2 и -3. Результатом будет список слов, встречающихся только в первом файле. И, если считать файл words.txt неким эталонным словарем, то выходящий список будет содержать слова, которых нет в словаре, то есть написанные с ошибками.

Немного истории

Идею программных каналов и значок вертикальной черты как их обозначение придумал Douglas McIlroy, один из авторов ранних командных оболочек. Он обратил внимание на то, сколько времени уходит на обработку вывода одной программы в качестве ввода другой. Его идеи были внедрены в жизнь, когда в 1973 Ken Thompson добавил программные каналы в операционную систему Юникс. Идея была со временем позаимствована другими ОС, такими как DOS, OS/2, Microsoft Windows, и BeOS, часто даже с тем же обозначением.

Понятие именованного канала

Английское название именованного канала — named pipe или FIFO (File In, File Out — файл пришел, файл ушел). Именованные каналы служат в основном для межпроцессного взаимодействия, когда различные процессы в системе обмениваются информацией. Тема это сложная и большая, заслуживающая отдельной статьи. Поэтому в данной работе я только вкратце коснусь ее.

В отличие от анонимного программного канала, автоматически создаваемого шеллом, именованный канал обладает именем, и создается явно при помощи команд mknod или mkfifo. Создадим именованный канал fifo1:

Теперь запустим процесс, обращающийся к данному каналу:

Несмотря на нажатие клавиши ENTER ничего не происходит, что не удивительно, ведь файл fifo1 пока пуст, и команде grep нечего обрабатывать. Однако консоль оказывается занята ждущим процессом, и разблокировать ее можно только прервав процесс (скажем, нажатием клавиш CTRL+c).

Чтобы наполнить именной канал содержимым, нужно чтобы к нему обратился второй процесс. Для этого мы должны открыть вторую консоль и запустить какую-либо команду, передающую данные в файл fifo1. Например:

Немедленно в первой консоли сработает команда grep:

Совершенно ясно, что пользоваться таким неудобным механизмом в пользовательских целях никто не будет, ведь гораздо проще запустить один программный канал:

и получить тот же результат.

Этот пример я привел лишь для демонстрации создания и работы именованного канала. Другое дело, когда именованные каналы создаются самими процессами для обмена информацией друг с другом. Но повторюсь, что тема эта непростая и в данной статье рассматриваться не будет.

Резюме

Программные каналы — мощнейший инструмент Юниксовидных операционных систем. Создание программных каналов многократно ускоряет процесс обработки данных, сокращает количество «писанины» в командной строке, а также позволяет получать результат в наиболее удобном виде.

Именованные каналы linux

Next: Блокировка файлов Up: Трубы (pipes) Previous: Функция popen() Contents

С помощью труб могут общаться только родственные друг другу процессы, полученные с помощью fork(). Именованные каналы FIFO позволяют обмениваться данными с абсолютно «чужим» процессом.

С точки зрения ядра ОС FIFO является одним из вариантов реализации трубы. Системный вызов mkfifo() предоставляет процессу именованную трубу в виде объекта файловой системы. Как и для любого другого объекта, необходимо предоставлять процессам права доступа в FIFO, чтобы определить, кто может писать, и кто может читать данные. Несколько процессов могут записывать или читать FIFO одновременно. Режим работы с FIFO — полудуплексный, т.е. процессы могут общаться в одном из направлений. Типичное применение FIFO — разработка приложений «клиент — сервер».

Синтаксис функции для создания FIFO следующий:

int mkfifo(const char *fifoname, mode_t mode); При возникновении ошибки функция возвращает -1, в противном случае 0. В качестве первого параметра указывается путь, где будет располагаться FIFO. Второй параметр определяет режим работы с FIFO. Пример использования приведен ниже:

int fd_fifo; /*дескриптор FIFO*/

char buffer[]=»Текстовая строка для fifon»;

/*Если файл с таким именем существует, удалим его*/

if((mkfifo(«/tmp/fifo0001.1», O_RDWR)) == -1)

fprintf(stderr, «Невозможно создать fifon»);

/*Открываем fifo для чтения и записи*/

if((fd_fifo=open(«/tmp/fifo0001.1», O_RDWR)) == — 1)

fprintf(stderr, «Невозможно открыть fifon»);

if(read(fd_fifo, &buf, sizeof(buf)) == -1)

fprintf(stderr, «Невозможно прочесть из FIFOn»);

printf(«Прочитано из FIFO : %sn»,buf);

Если в системе отсутствует функция mkfifo(), можно воспользоваться общей функцией для создания файла: int mknod(char *pathname, int mode, int dev);

Здесь pathname указывает обычное имя каталога и имя FIFO. Режим обозначается константой S_IFIFO из заголовочного файла . Здесь же определяются права доступа. Параметр dev не нужен. Пример вызова mknod:

if(mknod(«/tmp/fifo0001.1», S_IFIFO | S_IRUSR | S_IWUSR,

< /*Невозможно создать fifo */ Если при открытии FIFO через open() не указать режим O_NONBLOCK, то открытие FIFO блокируется и для записи, и для чтения. При записи канал блокируется до тех пор, пока другой процесс не откроет FIFO для чтения. При чтении канал снова блокируется до тех пор, пока другой процесс не запишет данные.

Флаг O_NONBLOCK может использоваться только при доступе для чтения. При попытке открыть FIFO с O_NONBLOCK для записи возникает ошибка открытия. Если FIFO закрыть для записи через close или fclose, это значит, что для чтения в FIFO помещается EOF.

Если несколько процессов пишут в один и тот же FIFO, необходимо обратить внимание на то, чтобы сразу не записывалось больше, чем PIPE_BUF байтов. Это необходимо, чтобы данные не смешивались друг с другом. Установить пределы записи можно следующей программой: #include

/*Создаем новый FIFO*/

if((mkfifo(«fifo0001», O_RDWR)) == -1)

fprintf(stderr, «Невозможно создать FIFOn»);

printf(«Можно записать в FIFO сразу %ld байтовn»,

printf(«Одновременно можно открыть %ld FIFO n»,

При попытке записи в FIFO, который не открыт в данный момент для чтения ни одним процессом, генерируется сигнал SIGPIPE.

В следующем примере организуется обработчик сигнала SIGPIPE, создается FIFO, процесс-потомок записывает данные в этот FIFO, а родитель читает их оттуда. Пример иллюстрирует простое приложение типа «клиент — сервер»:

static volatile sig_atomic_t sflag;

static sigset_t signal_new, signal_old, signal_leer;

static void sigfunc(int sig_nr)

fprintf(stderr, «SIGPIPE вызывает завершение

if(signal(SIGPIPE, sigfunc) == SIG_ERR)

fprintf(stderr, «Невозможно получить сигнал

/*Удаляем все сигналы из множества сигналов*/

/*Устанавливаем signal_new и сохраняем его*/

/* теперь маской сигналов будет signal_old*/

&signal_old) 0) /*Родитель читает из FIFO*/

if (( r_fifo=open(«/tmp/fifo0001.1», O_RDONLY))

Next: Блокировка файлов Up: Трубы (pipes) Previous: Функция popen() Contents 2004-06-22

Linux Exp Group

Введение в именованные каналы

Introduction to Named Pipes #41
September 1997
Sep 01, 1997 By Andy Vaught in SysAdmin

Весьма полезная особенность Linux, это возможность использовать именованные каналы в качестве одного из механизмов межпроцессного взаимодействия. «Пайпы» имеются во всех UNIX-подобных системах. Они позволяют различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это позволяет комбинировать различные утилиты в весьма причудливые конструкции.

Первое знакомство

Простейший пример использования канала:

Когда интерпретатор bash проверяет ввод и находит символ вертикальной черты, |, которая разделяет две команды, он запускает обе команды, соединяя вывод одной со вводом другой. Команда ls выводит список файлов в текущем каталоге, в то время как утилита grep проверяет строки этого списка и находит среди них только такие, которые содержат символ х.

Это был пример работы неименованного канала. Этот канал существует только в ядре и к нему нет доступа из процесса, который его породил, в данном случае, из экземпляра bash. Для тех, кто еще не знает, в UNIX новый процесс порождается уже существующим, в примере выше bash породил два дочерних процесса для выполнения программ ls и grep.

Читать еще:  Запуск процесса в linux

Другой вид каналов — это именованные каналы, которые иногда называют FIFO (поток данных в неименованном канале тоже обрабатывается по процедуре FIFO — прим. перев.). Имя именованного канала есть ни что иное, как имя файла на файловой системе. Именованные каналы показываются командой ls как обычные файлы с парой отличий:

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

Стандартная утилита для создания именованного канала — это mkfifo. Она принимает одно или более имя в качестве аргументов и создает каналы под этими именами. Например, для создания именованного канала под именем pipe1 следует отдать команду:

Простейший метод объяснения, как работают именованные каналы, это разобрать пример. Мы создали канал в примере выше. Теперь в первой виртуальной консоли командуем:

В свою очередь во второй виртуальной консоли введем следующую команду:

Вуаля! Вывод команды в первой консоли отобразится во второй консоли. Заметьте, что порядок выполнения команд значения не имеет. Также обратите внимание на то, что первая команда ничего не выводит. Это происходит потому, что процесс блокируется в ожидании события соединения выхода канала со входом в какой-нибудь другой процесс.

Одним из наиболее употребимых свойств каналов является возможность обеспечения коммуникации различных программ. Например, программа, обрабатывающая какие-либо запросы (на печать, обращения к базе данных, и тому подобные) может открыть канал на чтение. Далее, другой процесс может создавать запросы путем открытия канала на запись и записывания в него запроса. Похоже на клиент-серверное взаимодействие, не так ли? Если «клиент» ничего не пишет в канал, ничего не происходит. Также ничего не будет происходить, если «сервер» не слушает с другой стороны канала.

Канальный беспредел

Создайте два именованных канала, pipe1 и pipe2. Запустите команду:

На экране вы не увидите ничего, но если запустите утилиту top для просмотра статусов процессов, вы увидите обе команды cat, которые с маниакальным упорством копируют символ х туда и сюда в бесконечном цикле.

Если нажать ctrl-C для выхода из цикла, можно увидеть сообщение “broken pipe”. Эта ошибка возникает, когда процесс, пишущий в канал, видит, что процесс, читающий из канала, закрыл свою сторону канала. С момента, когда «читатель» перестает читать, данные перестают отправляться. В нормальной ситуации «писатель» должен первым закрыть входное соединение, тогда «читатель» получает EOF (конец файла) и завершает запрос.

Возникнет или нет ошибка “broken pipe”, зависит от событий в точный момент нажатия ctrl-C. Если второй cat в это время читает х, ctrl-C останавливает второй cat, канал 1 закрывается и первый cat завершается молча, то есть без сообщений. Если же второй cat ожидал записи первым х в канал, ctrl-C завершает второй cat перед тем, как первый сможет записать в канал, и возникает сообщение об ошибке. Такого рода рандомная конкуренция обычно обозначается термином “race condition”.

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

выведет две копии списка файлов текущего каталога в файл ls.out. Подстановка команд появляется тогда, когда мы ставим у левой команды. Введите, например:

Результат ls -l, которая выполняется в подоболочке, как обычно, но направляет свой вывод во временный именованный канал, который создала bash. Когда cat прочитает информацию из этого временного канала, он уничтожается. Таким образом, cat имеет регулярное имя файла, которое она может использовать для чтения из него. Подобным образом, задание в командной строке последовательности >(commands) породит временный именованный канал, из которого bash прочитает информацию для подачи ее на ввод последовательности commands.

Другой пример, если вы хотите посмотреть, есть ли в двух каталогах одинаковые файлы, введите последовательность:

Утилита cmp сравнит два списка и выведет не уникальные имена.

Подстановка команд также используется при работе утилиты tee, с помощью которой можно одновременно просматривать вывод команд и сохранять его в файл, при этом один поток данных может быть прочтен многими «читателями» без специальных усилий, bash все сама сделает для вас.

подсчитывает количество строк, слов и символов в файлах foo, bar и baz и складывает эти данный в файлы foo.count, bar.count и baz.count.

Подстановка команд может быть даже такой:

Это будет работать также, как просто вывод списка текущего каталога с помощью ls.

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

Каналы (pipe,fifo)

Каналы — неименованные (pipe) и именованные (fifo) — это средство передачи данных между процессами.

Можно представить себе канал как небольшой кольцевой буфер в ядре операционной системы. С точки зрения процессов, канал выглядит как пара открытых файловых дескрипторов – один на чтение и один на запись (можно больше, но неудобно). Мы можем писать в канал до тех пор пока есть место в буфере, если место в буфере кончится – процесс будет заблокирован на записи. Можем читать из канала пока есть данные в буфере, если данных нет – процесс будет заблокирован на чтении. Если закрыть дескриптор отвечающий за запись, то попытка чтения покажет конец файла. Если закрыть дескриптор отвечающий за чтение, то попытка записи приведет к доставке сигнала SIGPIPE и ошибке EPIPE.

При использовании канала в программировании на языке shell

блокировки чтения/записи обеспечивают синхронизацию скорости выполнения двух программ и их одновременное завершение.

Понятия позиции чтения/записи для каналов не существует, поэтому запись всегда производится в хвост буфера, а чтение с головы.

Для архитектуры i386 размер буфера, связанного с каналом устанавливают кратным размеру страницы (4096 байт). В Linux в версиях до 2.6.11 использовалась одна страница (4 КБ), после — 16 страниц (65 КБ), с возможностью изменения через fcntl . POSIX определяет значение PIPE_BUF, задающего максимальный размер атомарной записи. В Linux PIPE_BUF равен 4096 байт.

Неименованные каналы

Неименованный канал создается вызовом pipe, который заносит в массив int [2] два дескриптора открытых файлов. fd[0] – открыт на чтение, fd[1] – на запись (вспомните STDIN == 0, STDOUT == 1). Канал уничтожается, когда будут закрыты все файловые дескрипторы ссылающиеся на него.

В рамках одного процесса pipe смысла не имеет, передать информацию о нем в произвольный процесс нельзя (имени нет, а номера файловых дескрипторов в каждом процессе свои). Единственный способ использовать pipe – унаследовать дескрипторы при вызове fork (и последующем exec ). После вызова fork канал окажется открытым на чтение и запись в родительском и дочернем процессе. Т.е. теперь на него будут ссылаться 4 дескриптора. Теперь надо определиться с направлением передачи данных – если надо передавать данные от родителя к потомку, то родитель закрывает дескриптор на чтение, а потомок — дескриптор на запись.

Оставлять оба дескриптора незакрытыми плохо по двум причинам:

Родитель после записи не может узнать считал ли дочерний процесс данные, а если считал то сколько. Соответственно, если родитель попробует читать из pipe, то, вполне вероятно, он считает часть собственных данных, которые станут недоступными для потомка.

Если один из процессов завершился или закрыл свои дескрипторы, то второй этого не заметит, так как pipe на его стороне по-прежнему открыт на чтение и на запись.

Если надо организовать двунаправленную передачу данных, то можно создать два pipe.

Именованные каналы

Именованный канал FIFO доступен как объект в файловой системе. При этом, до открытия объекта FIFO на чтение, собственно коммуникационного объекта не создаётся. После открытия открытия объекта FIFO в одном процессе на чтение, а в другом на запись, возникает ситуация полностью эквивалентная использованию неименованного канала.

Объект FIFO в файловой системе создаётся вызовом функции int mkfifo(const char *pathname, mode_t mode); ,

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

Правила обмена через канал

При обмене данными через канал существуют два особых случая:

  1. Попытка чтения при отсутствии писателей
  2. Попытка записи при отсутствии читателей
Читать еще:  Установка и настройка сервера linux

Первый случай интерпретируется как конец файла и вызов read вернёт 0. Второй случай не имеет аналогов при работе с обычными файлами, а потому вызывает доставку сигнала SIGPIPE. Программы-фильтры, которые работают с STDOUT по сигналу SIGPIPE обычно завершают работу. Если программа расcчитана на работу с каналами, то для корректной обработки этой ситуации она должна явно изменить стандартный обработчик SIGPIPE, установив его в игнорирование сигнала или переназначив на свою функцию.

Правила обмена через канал

  1. При чтении числа байт, меньшего чем находится в канале, возвращается требуемое число байтов, остаток сохраняется для последующих чтений.
  2. При чтении числа байт, большего чем находится в канале, возвращается доступное число байт.
  3. При чтении из пустого канала, открытого каким либо процессом на на запись при сброшенном флаге O_NONBLOCK произойдёт блокировка процесса, а при установленном флаге O_NONBLOCK будет возвращено -1 и установлено значение errno равное EAGAIN.
  4. Если канал пуст и ни один процесс не открыл его на запись, то при чтении из канала будет получено 0 байтов — т.е конец файла.
  1. Если процесс пытается записать данные в канал, не открытый ни одним процессом на чтение, то процессу отправляется сигнал SIGPIPE. Если не установлена обработка сигнала, то процесс завершается, в противном случае вызов write() возвращает -1 с установкой ошибки EPIPE.
  2. Запись числа байт меньше чем PIPE_BUF выполняется атомарно. При записи из нескольких процессов данные не перемешиваются.
  3. При записи числа байт больше чем PIPE_BUF атомарность операции не гарантируется.
  4. Если флаг O_NONBLOCK не установлен, то запись может быть заблокирована, но в конце концов будет возвращено значение, указывающее, что все байты записаны.
  5. Если флаг O_NONBLOCK установлен и записывается меньше чем PIPE_BUF, то возможны два варианта: если есть достаточно свободного места в буфере, то производится атомарная запись, если нет, то возвращается -1, а errno выставляется в EAGAIN.
  6. Если флаг O_NONBLOCK установлен и записывается больше чем PIPE_BUF то возможны два варианта: если в буфере есть хотя бы один свободный байт, то производится запись доступного числа байт, если нет, то возвращается -1, а errno выставляется в EAGAIN.

Linux Exp Group

Введение в именованные каналы

Introduction to Named Pipes #41
September 1997
Sep 01, 1997 By Andy Vaught in SysAdmin

Весьма полезная особенность Linux, это возможность использовать именованные каналы в качестве одного из механизмов межпроцессного взаимодействия. «Пайпы» имеются во всех UNIX-подобных системах. Они позволяют различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это позволяет комбинировать различные утилиты в весьма причудливые конструкции.

Первое знакомство

Простейший пример использования канала:

Когда интерпретатор bash проверяет ввод и находит символ вертикальной черты, |, которая разделяет две команды, он запускает обе команды, соединяя вывод одной со вводом другой. Команда ls выводит список файлов в текущем каталоге, в то время как утилита grep проверяет строки этого списка и находит среди них только такие, которые содержат символ х.

Это был пример работы неименованного канала. Этот канал существует только в ядре и к нему нет доступа из процесса, который его породил, в данном случае, из экземпляра bash. Для тех, кто еще не знает, в UNIX новый процесс порождается уже существующим, в примере выше bash породил два дочерних процесса для выполнения программ ls и grep.

Другой вид каналов — это именованные каналы, которые иногда называют FIFO (поток данных в неименованном канале тоже обрабатывается по процедуре FIFO — прим. перев.). Имя именованного канала есть ни что иное, как имя файла на файловой системе. Именованные каналы показываются командой ls как обычные файлы с парой отличий:

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

Стандартная утилита для создания именованного канала — это mkfifo. Она принимает одно или более имя в качестве аргументов и создает каналы под этими именами. Например, для создания именованного канала под именем pipe1 следует отдать команду:

Простейший метод объяснения, как работают именованные каналы, это разобрать пример. Мы создали канал в примере выше. Теперь в первой виртуальной консоли командуем:

В свою очередь во второй виртуальной консоли введем следующую команду:

Вуаля! Вывод команды в первой консоли отобразится во второй консоли. Заметьте, что порядок выполнения команд значения не имеет. Также обратите внимание на то, что первая команда ничего не выводит. Это происходит потому, что процесс блокируется в ожидании события соединения выхода канала со входом в какой-нибудь другой процесс.

Одним из наиболее употребимых свойств каналов является возможность обеспечения коммуникации различных программ. Например, программа, обрабатывающая какие-либо запросы (на печать, обращения к базе данных, и тому подобные) может открыть канал на чтение. Далее, другой процесс может создавать запросы путем открытия канала на запись и записывания в него запроса. Похоже на клиент-серверное взаимодействие, не так ли? Если «клиент» ничего не пишет в канал, ничего не происходит. Также ничего не будет происходить, если «сервер» не слушает с другой стороны канала.

Канальный беспредел

Создайте два именованных канала, pipe1 и pipe2. Запустите команду:

На экране вы не увидите ничего, но если запустите утилиту top для просмотра статусов процессов, вы увидите обе команды cat, которые с маниакальным упорством копируют символ х туда и сюда в бесконечном цикле.

Если нажать ctrl-C для выхода из цикла, можно увидеть сообщение “broken pipe”. Эта ошибка возникает, когда процесс, пишущий в канал, видит, что процесс, читающий из канала, закрыл свою сторону канала. С момента, когда «читатель» перестает читать, данные перестают отправляться. В нормальной ситуации «писатель» должен первым закрыть входное соединение, тогда «читатель» получает EOF (конец файла) и завершает запрос.

Возникнет или нет ошибка “broken pipe”, зависит от событий в точный момент нажатия ctrl-C. Если второй cat в это время читает х, ctrl-C останавливает второй cat, канал 1 закрывается и первый cat завершается молча, то есть без сообщений. Если же второй cat ожидал записи первым х в канал, ctrl-C завершает второй cat перед тем, как первый сможет записать в канал, и возникает сообщение об ошибке. Такого рода рандомная конкуренция обычно обозначается термином “race condition”.

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

выведет две копии списка файлов текущего каталога в файл ls.out. Подстановка команд появляется тогда, когда мы ставим у левой команды. Введите, например:

Результат ls -l, которая выполняется в подоболочке, как обычно, но направляет свой вывод во временный именованный канал, который создала bash. Когда cat прочитает информацию из этого временного канала, он уничтожается. Таким образом, cat имеет регулярное имя файла, которое она может использовать для чтения из него. Подобным образом, задание в командной строке последовательности >(commands) породит временный именованный канал, из которого bash прочитает информацию для подачи ее на ввод последовательности commands.

Другой пример, если вы хотите посмотреть, есть ли в двух каталогах одинаковые файлы, введите последовательность:

Утилита cmp сравнит два списка и выведет не уникальные имена.

Подстановка команд также используется при работе утилиты tee, с помощью которой можно одновременно просматривать вывод команд и сохранять его в файл, при этом один поток данных может быть прочтен многими «читателями» без специальных усилий, bash все сама сделает для вас.

подсчитывает количество строк, слов и символов в файлах foo, bar и baz и складывает эти данный в файлы foo.count, bar.count и baz.count.

Подстановка команд может быть даже такой:

Это будет работать также, как просто вывод списка текущего каталога с помощью ls.

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

Ссылка на основную публикацию
Adblock
detector