【Linux 應用編程】進程管理 - 進程間通訊IPC之管道 pipe 和 FIFO

IPC(InterProcess Communication,進程間通訊)是進程中的重要概念。Linux 進程之間經常使用的通訊方式有:mysql

  • 文件:簡單,低效,須要代碼控制同步
  • 管道:使用簡單,默認阻塞
    • 匿名管道 pipe:只存在於內核緩衝區,只能用於有血緣關係的進程
    • 有名管道 FIFO:在文件系統中存在,可用於無血緣關係的進程
  • 信號量:使用複雜,但開銷小,操做系統自己支持信號量
  • 內存映射區 mmap:進程有無血緣關係均可以
  • 本地套接字 socket:穩定可靠

管道概念

經過管道,能夠把一個進程的輸出做爲另外一個進程的輸入。管道分爲兩種:web

  • 匿名管道:主要用於兩個進程間有父子關係的進程間通訊
  • 命名管道:主要用於沒有父子關係的進程間通訊

一般用的管道,都是匿名管道。管道其實是一塊內核緩衝區,不佔用磁盤空間,但操做方式跟文件是同樣的。sql

在 Linux 的命令行終端中,管道是再經常使用不過的命令技巧了。經過管道能夠把前一個命令的輸出做爲後一個命令的輸入。在 Linux 命令中一般經過符號 | 來使用管道。數組

例如,查看全部的進程,而後按關鍵字過濾:緩存

ps aux | grep mysql

匿名管道

匿名管道是不能在文件系統中以任何方式看到的半雙工管道,不佔磁盤空間。管道的特色有:bash

  • 一端只讀或只寫。讀端、寫端分別是一個文件描述符
  • 操做管道的進程退出後,管道自動釋放
  • 管道默認是阻塞的
  • 半雙工,數據在管道內只能單向傳輸

匿名管道沒法判斷消息是否被讀完,一般僅用於父進程向子進程傳遞數據,或子進程向父進程傳遞數據。此時能夠在父子進程中關閉不用的讀或寫端。數據結構

管道是基於環形隊列這個數據結構實現的,數據先進先出。默認的緩衝區大小是 4 KB,大小會自動調整。socket

  • 單工:數據單向流動,例如遙控器
  • 雙工:數據能夠同時雙向流動,例如手機
  • 半雙工:數據能夠雙向流動,但不可同時雙向流動,例如對講機

pipe 函數

pipe() 函數用於建立匿名管道。.svg

函數原型:函數

#include <unistd.h>

int pipe(int pipefd[2]);

參數:返回文件描述符數組,對應管道的兩端,往寫端寫的數據會被內核緩存起來,直到讀端將數據讀完。其中:

  • pipefd[0]:讀端
  • pipefd[1]:寫端

返回值:成功返回 0,不然返回-1。

示例:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int fd[2];
    int ret, pid;
    char buf;
    ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe error");
        exit(1);
    }

    pid = fork();
    if (pid == 0)
    {
        close(fd[1]);
        while(read(fd[0], &buf, 1) > 0)
        {
	        // 兩種方式均可以實現輸出
            printf("%c\n", buf);
            write(STDOUT_FILENO, &buf, 1);
        }
        close(fd[0]);
    } 
    else if (pid > 0)
    {
        close(fd[0]);
        write(fd[1], "hello world", 12);
        close(fd[1]);
        wait(NULL);
    } 
    else
    {
        perror("fork error");
        exit(1);
    }

    close(fd[0]);
    close(fd[1]);
    return 0;
}

命名管道

命名管道也叫 FIFO 文件,在文件系統中以文件名的形式存在,大小爲0。匿名管道沒法在無關進程之間通訊,但 FIFO 能夠藉助文件系統中的文件,使得同一主機內的全部的進程均可以通訊。

進程經過 FIFO 通訊時,首先要打開管道文件,而後使用 read 、write 函數通訊。

mkfifo 函數

函數原型:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

參數:

  • pathname:指定文件名
  • mode:指定文件的讀寫權限

返回值:
函數成功返回 0,不然返回-1 並設置 errno,常見 errno 有:

  • EACCES:pathname 所在的目錄不容許執行權限
  • EEXIST:pathname 已經存在
  • ENOENT:目錄部分不存在
  • ENOTDIR:目錄部分不一個目錄
  • EROFS:路徑指向一個只讀的文件系統

示例:
建立兩個進程,一個將文件內容讀到 FIFO,另外一個從 FIFO 讀內容寫到另外一個文件。
寫 FIFO 的文件示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFSIZE 1024

int main(int argc, char *argv[]){
    int ret;
    int datafd, fifofd;
    int bytes;
    char buffer[BUFSIZE];
    const char *fifoname = "/tmp/fifo";

    if (argc != 2) {
        printf("please input filename\n");
        exit(EXIT_FAILURE);
    }

    if (access(fifoname, F_OK) < 0) {
        ret = mkfifo(fifoname, 0777);
        if (ret < 0) {
            perror("mkfifo error");
            exit(EXIT_FAILURE);
        }
    }
    fifofd = open(fifoname, O_WRONLY);
    datafd = open(argv[1], O_RDONLY);
    if (fifofd < 0) {
        perror("open fifo error");
        exit(EXIT_FAILURE);
    }
    if (datafd < 0) {
        perror("open file error");
        exit(EXIT_FAILURE);
    }

    bytes = read(datafd, buffer, BUFSIZE);
    while (bytes > 0) {
        ret = write(fifofd, buffer, bytes);
        if (ret < 0) {
            perror("write fifo error");
            exit(EXIT_FAILURE);
        }
        bytes = read(datafd, buffer, BUFSIZE);
    }
    close(fifofd);
    close(datafd);
    return 0;
}

讀 FIFO 的文件示例:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFSIZE 1024

int main(int argc, char *argv[]) {
    char *fifoname = "/tmp/fifo";
    int fifofd, datafd;
    int bytes, ret;
    char buffer[BUFSIZE];

    if (argc != 2) {
        printf("please input a filename");
        exit(EXIT_FAILURE);
    }

    fifofd = open(fifoname, O_RDONLY);
    datafd = open(argv[1], O_WRONLY);
    if (fifofd < 0) {
        perror("open fifo error");
        exit(EXIT_FAILURE);
    }
    if (datafd < 0) {
        perror("open file error");
        exit(EXIT_FAILURE);
    }

    bytes = read(fifofd, buffer, BUFSIZE);
    while(bytes > 0) {
        ret = write(datafd, buffer, bytes);
        if (ret < 0) {
            perror("write file error");
            exit(EXIT_FAILURE);
        }
        bytes = read(fifofd, buffer, BUFSIZE);
    }
    close(datafd);
    close(fifofd);
    return 0;
}
相關文章
相關標籤/搜索