1. 程式人生 > >管道(PIPE)和命名管道(FIFO)

管道(PIPE)和命名管道(FIFO)

1.管道

管道是UNIX系統中最古老的 IPC 方法,管道通常用來在有關係的程序中傳遞資料,即父子程序間,父孫子程序間,兩個子程序間等(由於fork會複製父程序的檔案描述符)。管道是一個位元組流,也就是說它的資料沒有邊界(但可以在程式中實現獨立的固定大小資料的傳輸),它可以讀或寫任意大小的資料,但是資料是按先進先出的順序傳輸,而不能進行隨機訪問(如lseek)。

管道一般用兩個檔案描述符標記,fd[0]表示讀取端,fd[1]表示寫入端。程序讀取空的管道將會阻塞,直到有資料可讀。當管道的寫入端關閉時,管道的讀取端將讀到EOF標誌(即不會阻塞,read函式會返回0)。管道的容量為PIPE_BUF(在limits.h中定義的巨集,64KB),如果寫入大於管道容量大小的資料,將會引起阻塞,直到有空間可寫。使用fcntl(fd, F_SETPIPE_SZ, size)可以該變管道的容量(fd即管道的描述符),該函式會返回核心對管道的容量調整後的大小

UNIX標準中,管道是單向的,即只能一端進行讀操作(通常會關閉寫端),另一端進行寫操作(通常會關閉讀端)。需要雙向通訊時,一般需要建立兩個管道。

關閉一端的原因:

1.只有當管道的所有檔案描述符關閉時,管道才銷燬

2.只有當所有讀端關閉時,管道滿時寫端才會收到SIGPIPE訊號(預設會關閉程序)

3.只有當所有寫端關閉時,管道空時讀端才會收到EOF標誌

例子:

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

#define BUF_SIZE 5

void err_exit(char *s) 
{
        perror(s);
        exit(-1);
}

int main(int argc, char *argv[])
{
        int pfd[2]; /* 管道描述符 */
        char buf[BUF_SIZE];
        ssize_t numRead;

        if (pipe(pfd) == -1)/* 建立管道 */
                err_exit("pipe");
        switch (fork()) {
        case -1: 
                        err_exit("fork");
        case 0: /* 子程序,讀管道 */
                if (close(pfd[1]) == -1) /* 關閉寫端 */
                        err_exit("close - child");
                while(1) { 
                        printf("before read\n");
                        numRead = read(pfd[0], buf, BUF_SIZE);
                        if (numRead == -1) 
                                err_exit("child - read");
                        if (numRead == 0)
                                break; /* 讀到EOF */
                        printf("%s\n", buf);
                        printf("after read\n");
                }

                write(STDOUT_FILENO, "\n", 1);

                if (close(pfd[0]) == -1)
                        err_exit("close");
                return 0;
        default: /* 父程序,寫管道 */
                if (close(pfd[0]) == -1) /* 關閉讀端 */
                        err_exit("close - parent");
                if (write(pfd[1], "hello", 5) != 5)
                        printf("parent pipe failed write\n");
                sleep(3);
                if (write(pfd[1], "world", 5) != 5)
                        printf("parent pipe failed write\n");
                if (close(pfd[1]) == -1) /* 子程序將讀到 EOF */
                        err_exit("close");
                wait(NULL); /* 等待子程序結束 */
                exit(0);
        }
}

2.命名管道(FIFO)

命名管道(FIFO)則是管道的一種變種,它可以在任意程序間傳遞數。FIFO在檔案系統中建立FIFO檔案,並擁有檔名。對FIFO的操作,就和操作普通檔案一樣。

可以在命令列建立FIFO:

$ mkfifo [ -m mode ] pathname