FIFO для межпроцессного взаимодействия Linux

сервер Linux

1 Обзор

FIFO почти аналогичен конвейеру, поэтому FIFO также является потоком байтов. Порядок чтения из FIFO также такой же, как и порядок записи в FIFO. Емкость также ограничена, и это также может гарантировать, что работа запись не более PIPE_BUF байт атомарна Да суть FIFO тоже пайп, но направление передачи может быть двунаправленным.Самая большая разница между ними в том, что FIFO имеет имя в файловой системе, а метод открытия один и тот же как открытие обычного файла (используя open), чтобы можно было использовать FIFO для связи между несвязанными процессами (такими как клиент и сервер). (Если вы не знакомы с пайплайнами, вы можете прочитать другую мою статью о пайплайнахКанал межпроцессного взаимодействия Linux)

2. Создайте ФИФО

#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);//return 0 on success,or -1 on error
  • Параметр режима указывает разрешения нового FIFO, то есть права доступа к файлам (rw-rw----)
  • Параметр режима будет подвергнут операции XOR со значением umask в процессе, чтобы указать окончательное значение разрешения (поэтому обычно устанавливается umask(0)).

3. Откройте ФИФО

  • FIFO успешно создан, и любой процесс может открыть его, если он проходит нормальную проверку прав доступа к файлам с исходно установленным режимом.
readFd=open(pathname,O_RDONLY);//打开只读方式

writeFd=open(pathname,O_WRONLY);//打开只写方式
  • Открытие FIFO для чтения данных (флаг open() O_RDONLY) будет заблокировано до тех пор, пока другой процесс не откроет FIFO для записи данных (open() O_WRONLY). Соответственно, открытие FIFO для записи данных будет заблокировано до тех пор, пока другой процесс не откроет FIFO для чтения данных.

4. Единственный разумный способ использовать FIFO — настроить процесс чтения и процесс записи на обоих концах разума.

  • Это может гарантировать, что операция записи не более байтов PIPE_BUF каждый раз будет атомарной.При превышении байтов PIPE_BUF ядро ​​будет разделять сообщение, что может запутать сообщения, отправленные другими пишущими.Если есть только один пишущий, то Это ограничение можно игнорировать, не беспокоясь о путанице.

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

  • Использование FIFO в односерверных многоклиентских приложениях

Ядро серверной программы

// we get the permissions we want
    umask(0);
    if(mkfifo(SERVER_FIFO,S_IRUSR|S_IWUSR|S_IWGRP)==-1&&errno!=EEXIST){
        ERR_EXIT("mkfifo");
    }
    serveFd=open(SERVER_FIFO,O_RDONLY);
    if(serveFd==-1){
        ERR_EXIT("open");
    }

for(;;){
        //Read requests and send responses
        if (read(serveFd, &req, sizeof(struct request)) != sizeof(struct request)) {
                errMsg("ERROR reading request;discarding\n");
                continue;
        }
        //Open client FIFO (previously created by client)
        snprintf(clientFifo,CLIENT_FIFO_NAME_LEN,CLIENT_FIFO_TEMPLATE,(long)req.pid);
        clientFd=open(clientFifo,O_WRONLY);
        if(clientFd==-1){
           errMsg("open\n");
            continue;
        }
        
        //send response and close FIFO
        if(write(clientFd,&resp, sizeof(struct response))!= sizeof(struct response)){
            errMsg("Error writing to FIFO");
        }
        if(close(clientFd)==-1){
            errMsg("close");
        }
      
    }

Ядро клиентской программы

    //create our FIFO (before sending request,to avoid a race)
    umask(0);
    snprintf(clientFifo,CLIENT_FIFO_NAME_LEN,CLIENT_FIFO_TEMPLATE,(long)getpid());
    if(mkfifo(clientFifo,S_IRUSR|S_IWUSR|S_IWGRP)==-1&&errno!=EEXIST){
        ERR_EXIT("mkfifo");
    }
    serverFd=open(SERVER_FIFO,O_WRONLY);    
    if(serverFd==-1){
        ERR_EXIT("open");
    }   
    if (write(serverFd, &req, sizeof(struct request)) != sizeof(struct request)) {
            ERR_EXIT("write");
    }
    //open our FIFO,read and display response
    clientFd=open(clientFifo,O_RDONLY);
    if(clientFd==-1){
        ERR_EXIT("open");
    }
    if(read(clientFd,&resp, sizeof(struct response))!= sizeof(response)){
        ERR_EXIT("read");
    }
    if(close(clientFd)==-1){
        ERR_EXIT("close");
    }

5. Неблокирующий ввод-вывод

Когда процесс открывает один конец FIFO, процесс блокируется, если другой конец FIFO не был открыт. Но иногда блокировка нежелательна, вы можете указать O_NONBLOCK при вызове open().

fd=open("fifopath",O_RDONLY|O_NONBLOCK);
if(fd==-1){
    errExit("open");
}

5.1 Существует две цели использования флага O_NONBLOCK при открытии FIFO.

  • Это позволяет одному процессу открывать оба конца FIFO. Процесс сначала указывает флаг O_NONBLOCK при открытии FIFO для чтения данных, а затем открывает FIFO для записи данных.
  • Это предотвращает взаимоблокировки между процессами, которые открывают два FIFO.

Тупик между процессами, открывающими два FIFO

5.2 Неблокирующие операции чтения() и записи()

  • Флаг O_NONBLOCK влияет не только на семантику open(), но и на семантику последующих вызовов read() и write().

  • Флаг состояния O_NONBLOCK для открытых файлов может быть включен или отключен через fcntl().

включить флаг

int flags;
flags=fcntl(fd,F_GETFL);//Fetch open files status flags
flags|=O_NONBLOCK; // Enable O_NONBLOCK bit
fcntl(fd,F_SETFL,flags);// Update open files status flags

Отключить тег

flags=fcntl(fd,F_GETFL);
flags&=~O_NONBLOCK; //disable O_NONBLOCK bit
fcntl(fd,F_SETFL,flags);

6. Семантика чтения и записи в конвейерах и FIFO

Семантика чтения n байтов из канала или FIFO, содержащего p байтов

Семантика записи n байтов в канал или FIFO