LINUX處理序間通訊:PIPE與FIFO

來源:互聯網
上載者:User

 

PIPE

http://ldl.wisplus.net/2010/10/01/linux%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%EF%BC%9A%E7%AE%A1%E9%81%93/

 

概述:

int pipe(int pipefd[2]);
調用pipe函數在核心中開闢一塊緩衝區(稱為管道)用於單向通訊,它有一個讀端一個寫端,然後通過filedes參數傳給使用者程式兩個檔案描述符,filedes[0]指向PIPE的讀端,filedes[1]指向PIPE的寫端。所以在使用者程式看起來就像一個開啟的檔案,通過read(filedes[0]);
或者write(filedes[1]); 向這個檔案讀寫資料其實是在讀寫核心緩衝區

建立PIPE的基本步驟:

• 父進程調用pipe 開闢PIPE,得到兩個檔案描述符指向管道的兩端。
• 父進程調用fork 建立子進程,那麼子進程也有兩個檔案描述符指向同一管道。
• 父進程關閉管道讀端,子進程關閉管道寫端。父進程可以往PIPE裡寫,子進程可以從PIPE裡讀,PIPE是用環形隊列實現的,資料從寫端流入從讀端流出,這樣就實現了處理序間通訊

12345678910111213141516171819202122232425262728293031323334 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h> #include<string.h>   int main() {     char buf[20];     pid_t pid;     int fd[2];     int n;       pipe(fd); //建立管道     if ((pid = fork()) < 0) //fork子進程     {         perror("fork error");         exit(-1);     } else if (pid == 0) //子進程中     {         close(fd[1]); //關閉寫端         n = read(fd[0],buf,20);         write(STDOUT_FILENO,buf,n);         close(fd[0]); //關閉讀端         exit(0);     } else //父進程中     {         close(fd[0]); //關閉讀端         write(fd[1],"hello world",strlen("hello world"));         close(fd[1]); //關閉寫端         waitpid(pid,NULL,0);         exit(0);     } }
popen函數與pclose函數

標準IO函數庫提供了popen函數,它建立一個管道並啟動另外一個進程,該進程從該PIPE讀出標準輸入或將標準輸出寫入該PIPE。
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函數:先執行fork,然後調用exec(sh)以執行command,並且返回一個標準I/O檔案指標。(錯誤返回NULL)
如果type是”r”,則檔案指標串連到command的標準輸出,(該進程為讀段,command所指進程為寫端),參數”w”同理.
This command is passed to /bin/sh using the -c flag;
pclose函數:關閉由popen建立的標準I/O流,等待命令執行結束,然後返回shell的終止狀態(錯誤返回-1)

12345678910111213141516171819202122232425262728293031 #include<stdio.h> #include<stdlib.h> #include<unistd.h>   #define PAGER "${PAGER:-more}" //如果shell變數PAGER已經定義而且非空,則使用其值,否則使用字串more int main() {     char line[100];     FILE *fpin,*fpout;     if ((fpin = fopen("A.txt","r")) == NULL)     {        perror("can't open A.txt");        exit(-1);     }     if ((fpout = popen(PAGER,"w")) == NULL)     {         perror("popen error");         exit(-1);     }     while(fgets(line,100,fpin) != NULL)     {         if (fputs(line,fpout) == EOF)         {             perror("fputs error to pipe");             exit(-1);         }     }     if (pclose(fpout) == -1)         perror("pclose error");     exit(0); }
FIFO

FIFO即是命名PIPE,檔案系統中有個路徑名與之關聯。PIPE只能由有親緣關係的進程使用,它們共同的祖先進程建立了管道。但是,通過FIFO,不相關的進程也能交換資料

建立FIFO

int mkfifo(const char *pathname, mode_t mode);
mode為存取許可權(需結合進程的umask).一般的檔案I/O函數都可以用於FIFO
mkfifo函數已經隱含指定O_CREAT | O_EXCL,也就是說,要麼建立一個新的FIFO,要麼返回EEXIST錯誤(檔案已經存在)

刪除FIFO

int unlink(const char *pathname);
不同於PIPE,FIFO只有通過unlink才能從檔案系統中刪除

開啟FIFO

int open(const char *pathname, int flags);
使用open函數開啟FIFO,預設情況下沒有指定O_NONBLOCK標誌

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 //fifo_write.c #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<errno.h> #include<fcntl.h> #define FIFO_FILE "/tmp/myfifo"   int main() {     int fd = 0;     int n;     char buf[100];       if ((fd = open(FIFO_FILE,O_WRONLY | O_NONBLOCK)) < 0) //非阻塞方式開啟     {         perror("open error");         exit(-1);     }     while (1)     {         fgets(buf,100,stdin);         n = strlen(buf);         if ((n = write(fd,buf,n)) < 0)         {             if (errno == EAGAIN)                 printf("The FIFO has not been read yet.Please try later\n");         }     }     return 0; }   //fifo_read.c #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<errno.h>   #define FIFO_FILE "/tmp/myfifo"   int main() {     char buf[100];     int n = 0;     int fd;     if ((mkfifo(FIFO_FILE,S_IRWXU) < 0) && (errno != EEXIST)) //如果該fifo檔案不存在,建立之     {         perror("mkfifo error");         exit(-1);     }     if ((fd = open(FIFO_FILE,O_RDONLY | O_NONBLOCK)) < 0) //非阻塞方式開啟     {         perror("open error");         exit(-1);     }     while (1)     {         if ((n = read(fd,buf,100)) < 0)         {             if (errno == EAGAIN)             {                 printf("No data yet\n");             }         } else write(STDOUT_FILENO,buf,n);         sleep(1); //sleep     }     unlink(FIFO_FILE);     return 0; }
FIFO與PIPE的讀寫:

(1)對於PIPE或FIFO的write總是往末尾添加資料,對他們的read則總是從開頭返回資料。如果對PIPE或FIFO調用lseek,那就返回ESPIPE錯誤
(2)一個檔案描述符能以2中方式設定成非阻塞:(預設為阻塞)
• 調用open是可指定O_NONBLOCK標誌
• 如果檔案描述符已經開啟,那麼可以調用fcntl設定O_NONBLOCK標誌(PIPE只能採用這種方式)
(3)讀寫規則:
阻塞(預設設定):
唯讀open
• FIFO已經被唯寫開啟:成功返回
• FIFO沒有被唯寫開啟:阻塞到FIFO被開啟來寫
唯寫open
• FIFO已經被唯讀開啟:成功返回
• FIFO沒有被唯讀開啟:阻塞到FIFO被開啟來讀
從空PIPE或空FIFO中read
• FIFO或PIPE已經被唯寫開啟:阻塞到PIPE或FIFO中有資料或者不再為寫開啟著
• FIFO或PIPE沒有被唯寫開啟:返回0(檔案結束符)
write
• FIFO或PIPE已經被唯讀開啟:
寫入資料量不大於PIPE_BUF(保證原子性):有足夠空間存放則一次性全部寫入,沒有則進入睡眠,直到當緩衝區中有能夠容納要寫入的全部位元組數時,才開始進行一次性寫操作
寫入資料量大於PIPE_BUF(不保證原子性):緩衝區一有空閑地區,進程就會試圖寫入資料,函數在寫完全部資料後返回
• FIFO或PIPE沒有被唯讀開啟:給線程產生SIGPIPE(預設終止進程)

O_NONBLOCK設定:
唯讀open
• FIFO已經被唯寫開啟:成功返回
• FIFO沒有被唯寫開啟:成功返回
唯寫open
• FIFO已經被唯讀開啟:成功返回
• FIFO沒有被唯讀開啟:返回ENXIO錯誤
從空PIPE或空FIFO中read
• FIFO或PIPE已經被唯寫開啟:返回EAGAIN錯誤
• FIFO或PIPE沒有被唯寫開啟:返回0(檔案結束符)
write
• FIFO或PIPE已經被唯讀開啟:
寫入資料量不大於PIPE_BUF(保證原子性):有足夠空間存放則一次性全部寫入,沒有則返回EAGAIN錯誤(不會部分寫入)
寫入資料量大於PIPE_BUF(不保證原子性):有足夠空間存放則全部寫入,沒有則部分寫入,函數立即返回
• FIFO或PIPE沒有被唯讀開啟:給線程產生SIGPIPE(預設終止進程)

PIPE或FIFO若干額外的規則:
• 如果請求讀取的資料量多餘當前可用的資料量,那麼返回這些可用的資料
• 如果請求寫入的資料位元組數小於或等於PIPE_BUF,那麼write操作保證是原子的(O_NONBLOCK標誌的設定對原子性沒有影響)
• 當對PIPE或FIFO最後一個關閉時,仍在該PIPE或FIFO上的資料將被丟棄

FIFO與PIPE的限制:

• 它們是半雙工的(單向性),即資料只能在一個方向上流動。由進程A流向進程B或由進程B流向進程A。
• PIPE的讀寫端通過開啟的檔案描述符來傳遞,因此要通訊的兩個進程必須從它們的公用祖先那裡繼承PIPE檔案描述符。FIFO可以實現無關進程間的通訊。
• 一個進程在任意時刻開啟的最大檔案描述符個數OPEN_MAX(通過調用sysconf(_SC_OPEN_MAX)獲得)
• 可原子地寫往PIPE或FIFO的最大資料量PIPE_BUF(通常定義在limits.h)

小結:

• PIPE普遍用於SHELL中,不過也可以從程式中使用,往往是從子程式向父程式回傳資訊。使用PIPE時涉及的某些代碼(pipe、fork、close、exec和waitpid)可通過使用popen和pclose來避免,由它們處理具體細節並啟用一個shell
• FIFO與管道類似,但他們是用mkfifo建立的,之後需要用open開啟。開啟管道時必須小心,因為有許多規則制約著open的阻塞與否(甚至發生死結)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.