IPC : (1) pipe & fifo

PIPE & FIFO 簡介

Pipe 與 fifo 為 Linux IPC(Inter-process communication)的方式之一.

共同的部分為
(1) 建立一個單向的 data channel.
(2) 同樣使用 fd(file descriptor) 作為讀寫的媒介.
(3) 預設讀取端(read)沒資料讀取會被鎖住(block), 直到寫入端(write)寫入資料. 相反過來 pipe buffer 滿了, write 會被鎖住(block). 若想改為非鎖住(non-block)則可透過 fcntl 或於 API 設置相關 flag 參數(如果 API 提供)

存取限制的部分
(1) pipe 僅舉限於關聯的 processes.
(2) fifo 則是透過 permission mask 來作為限制.

相關的特性如下表所示[1].

類型 識別名稱 於系統的表示 存取特性 persistence
pipe no name fd Only by related processes process
FIFO path name fd permission mask process (?)

API 範例

以下針對兩個 API 個別進行說明. 其個別完整的範例程式可於 github 看到

Pipe[2]

API 說明

#include <unistd.h>

int pipe(int pipefd[2]);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>

int pipe2(int pipefd[2], int flags);

pipe 與 pipe2 API 會建立一個單向的 data channel.
pipefd[0] 作為 pipe 的讀取端(read),
pipefd[1] 作為 piep 的寫入端(write).

pipe2 API 額外新增一個 flag 參數. 可參考下方說明[2]

O_NONBLOCK
    Set the O_NONBLOCK file status flag on the two new open file descriptions. Using this flag saves extra calls to fcntl(2) to achieve the same result.
O_CLOEXEC
    Set the close-on-exec (FD_CLOEXEC) flag on the two new file descriptors. See the description of the same flag in open(2) for reasons why this may be useful.

API 範例

int pipefd[2] = {0};
char buf;

// create pipe
if (-1 == pipe(pipefd)) 
{
 ...
}

switch(fork())
{
 // Child reads from pipe
 case 0:
  close(pipefd[1]);          
  read(pipefd[0], &buf, 1);
                close(pipefd[0]);       
                ....
       // parent write data to pipe
 default: 
  close(pipefd[0]);          
  write(pipefd[1], SZ_HELLO, strlen(SZ_HELLO));
  close(pipefd[1]);        
                ....  
}

帶入一個 int array 至 pipe API, 藉此產生一個 pipe channel. 再透過 fork 複製一個 process 出來. 由 child 負責讀取(pipefd[0]). parent 負責寫入(pipefd[1]).

FIFO[3]

API 說明

#include <sys/types.h>#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

mkdifo API 類似 pipe(pipe2) 都是產生單方向的 data channel. 不同點是
(1) 藉由 pathname 建立一個可識別的特殊 fifo 檔案. 知道此 pathname 的 process 使用 open(const char *path, int oflag, ...) 打開此 fifo 檔案, 對檔案進行讀取或寫入.
(2) mode 參數設置此 fifo 檔案的權限為何, 限制其餘 process 的存取限制.

API 範例

#define SZ_FIFO_PATH    "/tmp/fifo-example"

switch(fork())
{
    // child => read data
    case 0:
        // wait a comment for creating fifo
        sleep(1);
        // open
        int fdFifo = 0;
        if(-1 == (fdFifo = open(SZ_FIFO_PATH, O_RDONLY)))
        {
            ...
        }
        // reading
        char buf;
        while(read(fdFifo, &buf, 1) > 0)
            write(STDOUT_FILENO, &buf, 1);
        write(STDOUT_FILENO, "\n", 1);
        ....
        break;

    // parent => craete fifo and write data
    default:
        // create fifo with name
        if(-1 == mkfifo(SZ_FIFO_PATH, S_IRWXU))
        {
            ...
        }

        // open fifo
        int fdFifo = 0;
        if(-1 == (fdFifo = open(SZ_FIFO_PATH, O_WRONLY)))
        {
            ...
        }
        // writing
        write(fdFifo, "hello", strlen("hello"));
        ...
        break;
}

先 fork child process 出來. parent 負責建立 fifo, 並寫入資料到 fifo 檔案. child 則是等待一會. 接著打開 fifo 檔案, 並開始讀取資料.

Reference

  1. An introduction to Linux IPC
  2. pipe
  3. fifo

留言

這個網誌中的熱門文章

yocto recipe : (1) 撰寫 recipe

yocto recipe : (2) 撰寫 bbappend

yocto recipe : (3) 使用 External Source 來編譯軟體