linux 进程间通讯之FIFO

1.概述

FIFO与管道几乎相似,因此FIFO也是一个字节流,从FIFO读取的顺序也是与被写入FIFO的顺序一致,容量是也有限的,也是能够确保写入不超过PIPE_BUF字节的操做是原子的,FIFO的本质也是一个管道,但传递方向是能够双向的,它们二者之间的最大差异在于FIFO在文件系统中拥有一个名称,而且打开方式与打开一个普通文件是同样的(使用open),这样就可以将FIFO用于非相关进程之间的通讯(如客户端和服务器)。(不熟悉管道的能够看个人另外一篇文章讲述管道linux 进程间通讯之管道linux

2.建立FIFO

#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);//return 0 on success,or -1 on error
复制代码
  • mode 参数指定了新FIFO的权限即文件权限(rw-rw----)
  • mode 参数会与进程中的umask值进行异或来指定最终的权限数值(因此通常设置umask(0))

3.打开FIFO

  • FIFO被建立成功,任何进程都可以打开它,只要它可以经过常规的文件权限检测即最初设置的mode。
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字节,内核会对消息进行拆分,那么就有可能混淆与其余写者发送的消息,若是只有一个写者则不用担忧混淆便可以忽略这个限制。bash

  • 多个客户端从FIFO中读取数据时会相互竞争,这样就可能会出某个客户端读取到其余客户端的响应消息。服务器

  • 在单服务器、多客户端应用程序中使用FIFO post

服务端程序核心ui

// 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");
        }
      
    }

复制代码

客户端程序核心spa

//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.非阻塞I/O

当一个进程打开一个FIFO的一端时,若是FIFO的另外一端尚未被打开,则该进程会被阻塞。但有些时候阻塞并非指望的行为,能够经过调用open()时指定O_NONBLOCK3d

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

5.1 打开一个FIFO时使用O_NONBLOCK标记存在两个目的

  • 它容许单个进程打开一个FIFO的两端。这个进程首先会在打开FIFO时指定O_NONBLOCK标记以便读取数据,接着打开FIFO以便写入数据。
  • 它防止打开两个FIFO的进程之间产生死锁。

打开两个FIFO的进程之间的死锁

5.2 非阻塞read()和write()

  • O_NONBLOCK 标记不只会影响open()的语义,还会影响后续的read()和write()调用语义。code

  • 能够经过fcntl() 启用或禁用打开着的文件的O_NONBLOCK状态的标记。cdn

启用标记server

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中read和write的语义

从一个包含p字节的管道或FIFO中读取n字节的语义

向一个管道或FIFO写入n字节的语义

相关文章
相关标签/搜索