Letysite.ru

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

Mkfifo linux пример

Mkfifo 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.

Другой вид каналов — это именованные каналы, которые иногда называют 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 все сама сделает для вас.

Читать еще:  Управление памятью linux

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

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

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

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

Прочитайте онлайн Основы программирования в Linux | Именованные каналы: FIFO

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

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

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

Вы можете создавать именованные каналы из командной строки и внутри программы. С давних времен программой создания их в командной строке была команда mknod :

$ mknod имя_файла p

Однако команды mknod нет в списке команд X/Open, поэтому она включена не во все UNIX-подобные системы. Предпочтительнее применять в командной строке

$ mkfifo имя_файла

У некоторых более старых версий UNIX была только команда mknod . В стандарте X/Open issue 4 Version 2 есть вызов функции mknod , но не программа командной строки. ОС Linux, как всегда настроенная дружелюбно, предлагает оба варианта: mknod и mkfifo .

Внутри программы можете применять два разных вызова:

#include

#include

int mkfifo(const char *filename, mode_t mode);

int mknod(const char* filename, mode_t mode | S_IFIFO, (dev_t)0);

Помимо команды mknod вы можете использовать функцию mknod для создания файлов специальных типов. Единственный переносимый вариант применения этой функции, создающий именованный канал, — использование значения 0 типа dev_t и объединений с помощью операции or режима доступа к файлу и S_IFIFO . В примерах мы будем применять более простую функцию mkfifo .

Итак, выполните упражнение 13.9.

Упражнение 13.9. Создание именованного канала

Далее приведен исходный текст примера fifo1.c.

int res = mkfifo(«/tmp/my_fifo», 0777);

if (res == 0) printf («FIFO createdn»);

Вы можете создать канал и заглянуть в него:

$ ./fifo1

$ ls -lF /tmp/my_fifo

prwxr-xr-x 1 rick users 0 2007-06-16 17:18 /tmp/my_fifo|

Обратите внимание на то, что первый символ вывода — р , обозначающий канал. Символ | в конце добавлен опцией -F команды ls и тоже обозначает канал.

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

Программа применяет функцию mkfifo для создания специального файла. Несмотря на то, что запрашиваете режим 0777 , он заменяется пользовательской маской ( umask ), устанавливаемой (в данном случае 022 ) точно так же, как при создании обычного файла, поэтому у результирующего файла режим 755 . Если ваша umask установлена иначе, например, ее значение 0002 , вы увидите другие права доступа у созданного файла.

Удалить FIFO можно как традиционный файл с помощью команды rm или внутри программы посредством системного вызова unlink .

У именованных каналов есть одно очень полезное свойство: поскольку они появляются в файловой системе, их можно применять в командах на месте обычного имени файла. Прежде чем вы продолжите программирование с использованием созданного вами файла FIFO, давайте исследуем поведение такого файла с помощью обычных команд для работы с файлом (упражнение 13.10).

Упражнение 13.10. Организации доступа к файлу FIFO

1. Сначала попробуйте прочесть (пустой) файл FIFO:

2. Теперь попытайтесь записать в FIFO. Вам придется использовать другой терминал, поскольку первая команда в данный момент «зависла» в ожидании появления каких-нибудь данных в FIFO:

$ echo «Hello World» > /tmp/my_fifo

Вы увидите вывод команды cat . Если не посылать никаких данных в канал FIFO, команда cat будет ждать до тех пор, пока вы не прервете ее выполнение, традиционно комбинацией клавиш + .

3. Можно выполнить обе команды одновременно, переведя первую в фоновый режим:

$ echo «Hello World» > /tmp/my_fifo

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

Поскольку в канале FIFO не было данных, обе команды, cat и echo , приостанавливают выполнение, ожидая, соответственно, поступления каких-нибудь данных и какого-либо процесса для их чтения.

На третьем шаге процесс cat с самого начала заблокирован в фоновом режиме. Когда echo делает доступными некоторые данные, команда cat читает их и выводит в стандартный вывод. Обратите внимание на то, что она затем завершается, не дожидаясь дополнительных данных. Программа cat не блокируется, т.к. канал уже закрылся, когда завершилась вторая команда, поместившая данные в FIFO, поэтому вызовы read в программе cat вернут 0 байтов, обозначая этим конец файла.

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

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

Открытие FIFO с помощью open

Основное ограничение при открытии канала FIFO состоит в том, что программа не может открыть FIFO для чтения и записи с режимом O_RDWR . Если программа нарушит это ограничение, результат будет непредсказуемым. Это очень разумное ограничение, т.к., обычно канал FIFO применяется для передачи данных в одном направлении, поэтому нет нужды в режиме O_RDWR . Процесс стал бы считывать обратно свой вывод, если бы канал был открыт для чтения/записи.

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

Другое различие между открытием канала FIFO и обычного файла заключается в использовании флага open_flag (второй параметр функции open ) со значением O_NONBLOCK . Применение этого режима open изменяет способ обработки не только вызова open , но и запросов read и write для возвращаемого файлового дескриптора.

Существует четыре допустимых комбинации значений O_RDONLY , O_WRONLY и O_NONBLOCK флага. Рассмотрим их все по очереди.

open(const char *path, O_RDONLY);

В этом случае вызов open блокируется, он не вернет управление программе до тех пор, пока процесс не откроет этот FIFO для записи. Это похоже на первый пример с командой cat .

open(const char *path, O_RDONLY | O_NONBLOCK);

Теперь вызов open завершится успешно и вернет управление сразу, даже если канал FIFO не был открыт для записи каким-либо процессом.

open(const char *path, O_WRONLY);

В данном случае вызов open будет заблокирован до тех пор, пока процесс не откроет тот же канал FIFO для чтения.

open(const char *path, O_WRONLY | O_NONBLOCK);

Этот вариант вызова всегда будет возвращать управление немедленно, но если ни один процесс не открыл этот канал FIFO для чтения, open вернет ошибку, -1, и FIFO не будет открыт. Если есть процесс, открывший FIFO для чтения, возвращенный файловый дескриптор может использоваться для записи в канал FIFO.

Обратите внимание на асимметрию в использовании O_NONBLOCK с O_RDONLY и O_WRONLY , заключающуюся в том, что неблокирующий вызов open для записи завершается аварийно, если ни один процесс не открыл канал для чтения, а неблокирующий вызов open для чтения не возвращает ошибку. На поведение вызова close флаг O_NONBLOCK влияния не оказывает.

Выполните упражнение 13.11.

Упражнение 13.11. Открытие файлов FIFO

Теперь рассмотрим, как можно использовать поведение вызова open с флагом, содержащим O_NONBLOCK , для синхронизации двух процессов. Вместо применения нескольких программ-примеров вы напишите одну тестовую программу fifo2.c, которая позволит исследовать поведение каналов FIFO при передаче ей разных параметров.

1. Начните с заголовочных файлов, директивы #define и проверки правильности количества предоставленных аргументов командной строки:

Mkfifo linux пример

Администратор

Группа: Главные администраторы
Сообщений: 14349
Регистрация: 12.10.2007
Из: Twilight Zone
Пользователь №: 1

Межпроцессное взаимодействие (Inter-process communication (IPC)) — это набор методов для обмена данными между потоками процессов. Процессы могут быть запущены как на одном и том же компьютере, так и на разных, соединенных сетью. IPC бывают нескольких типов: «сигнал», «сокет», «семафор», «файл», «сообщение»…

В данной статье я хочу рассмотреть всего 3 типа IPC:

  1. именованный канал
  2. разделенная память
  3. семафор

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

Для передачи сообщений можно использовать механизмы сокетов, каналов, D-bus и другие технологии. Про сокеты на каждом углу можно почитать, а про D-bus отдельную статью написать. Поэтому я решил остановиться на малоозвученных технологиях отвечающих стандартам POSIX и привести рабочие примеры.

Читать еще:  Linux новый пользователь

Рассмотрим передачу сообщений по именованным каналам. Схематично передача выглядит так:

Для создания именованных каналов будем использовать функцию, mkfifo():

int mkfifo(const char *pathname, mode_t mode);

Функция создает специальный FIFO файл с именем pathname, а параметр mode задает права доступа к файлу.

Примечание: [b]mode используется в сочетании с текущим значением umask следующим образом: (mode &

umask). Результатом этой операции и будет новое значение umask для создаваемого нами файла. По этой причине мы используем 0777 (S_IRWXO | S_IRWXG | S_IRWXU), чтобы не затирать ни один бит текущей маски.[/b]

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

В случае успешного создания FIFO файла, mkfifo() возвращает 0 (нуль). В случае каких либо ошибок, функция возвращает -1 и выставляет код ошибки в переменную errno.

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

  • EACCES — нет прав на запуск (execute) в одной из директорий в пути pathname
  • EEXIST — файл pathname уже существует, даже если файл — символическая ссылка
  • ENOENT — не существует какой-либо директории, упомянутой в pathname, либо является битой ссылкой
  • ENOSPC — нет места для создания нового файла
  • ENOTDIR — одна из директорий, упомянутых в pathname, на самом деле не является таковой
  • EROFS — попытка создать FIFO файл на файловой системе «только-на-чтение»

Чтение и запись в созданный файл производится с помощью функций read() и write().

mkfifo.c
#include
#include
#include
#include

#define NAMEDPIPE_NAME «/tmp/my_named_pipe»
#define BUFSIZE 50

int main (int argc, char ** argv) <
int fd, len;
char buf[BUFSIZE];

if ( mkfifo(NAMEDPIPE_NAME, 0777) ) <
perror(«mkfifo»);
return 1;
>
printf(«%s is createdn», NAMEDPIPE_NAME);

if ( (fd = open(NAMEDPIPE_NAME, O_RDONLY)) /tmp/my_named_pipe

В результате мы увидим следующий вывод от программы:

$ ./mkfifo
/tmp/my_named_pipe is created
/tmp/my_named_pipe is opened
Incomming message (22): Hello, my named pipe!
read: Success

Следующий тип межпроцессного взаимодействия — разделяемая память (shared memory). Схематично изобразим ее как некую именованную область в памяти, к которой обращаются одновременно два процесса:

Для выделения разделяемой памяти будем использовать POSIX функцию shm_open():

int shm_open(const char *name, int oflag, mode_t mode);

Функция возвращает файловый дескриптор, который связан с объектом памяти. Этот дескриптор в дальнейшем можно использовать другими функциями (к примеру, mmap() или mprotect()).

Целостность объекта памяти сохраняется, включая все данные связанные с ним, до тех пор пока объект не отсоединен/удален (shm_unlink()). Это означает, что любой процесс может получить доступ к нашему объекту памяти (если он знает его имя) до тех пор, пока явно в одном из процессов мы не вызовем shm_unlink().

Переменная oflag является побитовым «ИЛИ» следующих флагов:

  • O_RDONLY — открыть только с правами на чтение
  • O_RDWR — открыть с правами на чтение и запись
  • O_CREAT — если объект уже существует, то от флага никакого эффекта. Иначе, объект создается и для него выставляются права доступа в соответствии с mode.
  • O_EXCL — установка этого флага в сочетании с O_CREATE приведет к возврату функцией shm_open ошибки, если сегмент общей памяти уже существует.

Как задается значение параметра mode подробно описано в предыдущем параграфе «передача сообщений».

После создания общего объекта памяти, мы задаем размер разделяемой памяти вызовом ftruncate(). На входе у функции файловый дескриптор нашего объекта и необходимый нам размер.

Следующий код демонстрирует создание, изменение и удаление разделяемой памяти. Так же показывается как после создания разделяемой памяти, программа выходит, но при следующем же запуске мы можем получить к ней доступ, пока не выполнен shm_unlink().

shm_open.c
#include
#include
#include
#include
#include
#include

#define SHARED_MEMORY_OBJECT_NAME «my_shared_memory»
#define SHARED_MEMORY_OBJECT_SIZE 50
#define SHM_CREATE 1
#define SHM_PRINT 3
#define SHM_CLOSE 4

void usage(const char * s) <
printf(«Usage: %s [‘text’]n», s);
>

int main (int argc, char ** argv) <
int shm, len, cmd, mode = 0;
char *addr;

sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

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

Имя семафора строится следующим образом: в начале идет символ «/» (косая черта), а следом латинские символы. Символ «косая черта» при этом больше не должен применяться. Длина имени семафора может быть вплоть до 251 знака.

Если нам необходимо создать семафор, то передается управляющий флаг O_CREATE. Чтобы начать использовать уже существующий семафор, то oflag равняется нулю. Если вместе с флагом O_CREATE передать флаг O_EXCL, то функция sem_open() вернет ошибку, в случае если семафор с указанным именем уже существует.

Параметр mode задает права доступа таким же образом, как это объяснено в предыдущих главах. А переменной value инициализируется начальное значение семафора. Оба параметра mode и value игнорируются в случае, когда семафор с указанным именем уже существует, а sem_open() вызван вместе с флагом O_CREATE.

Для быстрого открытия существующего семафора используем конструкцию:

sem_t *sem_open(const char *name, int oflag);, где указываются только имя семафора и управляющий флаг.

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

sem_open.c
#include
#include
#include
#include

#define SEMAPHORE_NAME «/my_named_semaphore»

int main(int argc, char ** argv) <
sem_t *sem;

if ( argc == 2 ) <
printf(«Dropping semaphore. n»);
if ( (sem = sem_open(SEMAPHORE_NAME, 0)) == SEM_FAILED ) <
perror(«sem_open»);
return 1;
>
sem_post(sem);
perror(«sem_post»);
printf(«Semaphore dropped.n»);
return 0;
>

if ( (sem = sem_open(SEMAPHORE_NAME, O_CREAT, 0777, 0)) == SEM_FAILED ) <
perror(«sem_open»);
return 1;
>

printf(«Semaphore is taken.nWaiting for it to be dropped.n»);
sem_wait(sem);
perror(«sem_wait»);
sem_close(sem);
perror(«sem_close»);

В одной консоли запускаем:

$ ./sem_open
Semaphore is taken.
Waiting for it to be dropped. <-- здесь процесс в ожидании другого процесса
sem_wait: Success
sem_close: Success

В соседней консоли запускаем:

$ ./sem_open 1
Dropping semaphore.
sem_post: Success
Semaphore dropped.

В следующих статьях я хочу рассмотреть технологии d-bus и RPC. Если есть интерес, дайте знать.

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

Содержание

Предисловие

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

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

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

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

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

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

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

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

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

Читать еще:  Команда passwd linux

Того же результата можно достичь, если сначала перенаправить вывод команды 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, в котором содержатся все слова английского языка (разумеется, такого файла у вас нет, но можно позаимствовать список слов из какого-нибудь словаря; а английского — чтобы не путаться с кодировками). Тогда составляется следующий программный канал:

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

Команда первая: 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:

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

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

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

Резюме

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

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