天天看點

Linux程序間通信二--有名管道FIFO

一、有名管道概念

  上一節對無名管道做了闡述,無名管道隻能用在兩個有親緣關系的程序直接。而有名管道FIFO可以用在任意兩程序之間,它依賴于檔案系統,是一個存在的特殊檔案。

  有名管道和普通檔案一樣具有磁盤存放路徑。檔案權限和其他屬性。但是,有名管道和普通檔案又有差別,有名管道并沒有在磁盤正存放真正的資訊,它存儲的通信資訊在記憶體中,兩程序結束後自動丢失,擁有一個磁盤路徑僅僅是一個接口,其目的是使程序間資訊的程式設計更簡單統一。通信的兩個程序結束後,有名管道的檔案路徑本身仍然存在,這是和無名管道不一樣的地方。有名管道的特性可以總結為一下幾點:

  1.可以用于同一台PC下任意兩程序通信;

  2.無法seek,要保持先入先出(FIFO)原則;

  3.有名管道通信是單項的;

  4.有名管道本身不會随通信雙方的結束而消失,但有名管道中的内容會随着通信雙方結束消失。

二、建立有名管道

  

mkfifo()

函數用來建立有名管道,其函數聲明如下:

// come from /usr/include/sys/stat.h
/*Create a new FIFO named PATH, with permission bits MODE*/
int mkfifo(const char* path, __mode_t mode);
           

  第一個參數path為要建立的管道檔案名;

  第二個參數mode為生成檔案的模式。

  

  

mkfifo()

會根據參數建立特殊的有名管道檔案,該檔案必須不存在。

mkfifo()

建立的FIFO檔案其他程序都可以用讀寫一般檔案的方式存取。當使用open()函數打開FIFO檔案是,O_NONBLOCK會有影響。

三、讀寫有名管道

  有名管道的實質和無名管道一樣是一段核心管理的記憶體空間。但在通過write和read系統調用來執行讀寫操作前,需要調用open()函數來打開該檔案。另外,操作無名無名管道的阻塞位置為open()位置,而不是有名管道的讀寫位置。

1.打開有名管道

  如果希望以寫的方式打開管道,則需要另一個程序以讀的方式打開管道,否則一直阻塞。反過來如果以讀的方式打開管道,則需要另一程序以寫的方式打開管道,否則一直阻塞。即,如果以某種方式打開有名管道,則系統将阻塞程序,直到有另一個程序(包括自己)以另一種方式打開該管道後才會繼續執行。顯然,一個程序以可讀可寫方式打開管道,目前程序充當了讀和寫兩個身份,程序不會阻塞。

2.兩程序已經完成打開管道操作,阻塞讀操作按以下方式執行:

  a.如果管道中沒有資料,讀操作預設阻塞;

  b.如果管道中有資料,但小于期望讀取資料量,讀出所有資料傳回;

  c.如果管道中有資料,但大于期望讀取資料量,讀出期望大小資料傳回。

3.兩程序已經完成打開管道操作,阻塞寫操作按以下方式執行:

  a.如果管道中沒有空間,寫操作阻塞;

  b.如果管道中有空間,但小于期望寫入資料量,寫滿空間後阻塞;

  c.如果管道中有空間,但大于期望寫入資料量,寫入資料後傳回。

4.兩程序已經完成打開管道操作,中途其中一個程序退出:

  a.未退出一端如果是寫操作,将傳回SIGPIPE信号;

  b.未退出一端如果是阻塞讀操作,讀操作将不再阻塞,直接傳回0。

四、示例

  下面給出在父子程序間使用FIFO通信的示例,在非情緣關系的程序間原理是一樣的:

#include <stdio.h>  
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>

#define FIFO_PATH   "./fifo"

void handler(int sig)
{
    printf("sig=%d\n", sig);
}


int main()  
{  
    signal(SIGPIPE, handler);
    unlink(FIFO_PATH);          // 删除原來的管道
    mkfifo(FIFO_PATH, );    // 建立管道

    pid_t pid = fork();

    if (pid < ) {
        perror("fork error");
        return -;
    }

    if ( == pid) {     // 子程序
        int fd;
        fd = open(FIFO_PATH, O_WRONLY);
        write(fd, "hello world!", );
        sleep();
        close(fd);
    } else {            // 父程序
        int fd;
        char buf[] = {0};
        fd = open(FIFO_PATH, O_RDONLY);
        int ret = read(fd, buf, sizeof(buf));

        printf("ret=%d buf=%s\n", ret, buf);
        close(fd);
    }

    return ;
}
           

運作結果如下所示:

Linux程式間通信二--有名管道FIFO

繼續閱讀