Основное ограничение при открытии канала 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
и проверки правильности количества предоставленных аргументов командной строки:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc, char *argv[]) {
int res;
int open_mode = 0;
int i;
if (argc < 2) {
fprintf(stderr, "Usage: %s <some combination of\
O_RDONLY O_WRONLY O_NONBLOCK>\n", *argv);
exit(EXIT_FAILURE);
}
2. Полагая, что программа передает тестовые данные, вы задаете параметр
open_mode
из следующих аргументов:
for(i = 1; i <argc; i++) {
if (strncmp(*++argv, "O_RDONLY", 8) == 0) open_mode |= O_RDONLY;
if (strncmp(*argv, "O_WRONLY", 8) == 0) open_mode |= O_WRONLY;
if (strncmp(*argv, "O_NONBLOCK", 10) == 0) open_mode |= O_NONBLOCK;
}
3. Далее проверьте, существует ли канал FIFO, и при необходимости создайте его. Затем FIFO открывается, и пока программа засыпает на короткое время, выполняется результирующий вывод. В заключение FIFO закрывается.
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Gould not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIF0\n", getpid);
res = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid, res);
sleep(5);
if (res != -1) (void)close(res);
printf("Process %d finished\n", getpid);
exit(EXIT_SUCCESS);
}
Как это работает
Эта программа позволяет задать в командной строке комбинации значений
O_RDONLY
,
O_WRONLY
и
O_NONBLOCK
, которые вы хотите применить. Делается это сравнением известных строк с параметрами командной строки и установкой (с помощью
|=
) соответствующего флага при совпадении строки. В программе используется функция
access
, проверяющая, существует ли уже файл FIFO, и создающая его при необходимости.