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字節的語義

相關文章
相關標籤/搜索