管道(PIPE)和命名管道(FIFO)
阿新 • • 發佈:2019-02-09
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