管道是Unix系統(tǒng)IPC的最古老形式,所有Unix系統(tǒng)都提供這種形式。管道有以下兩種局限性:
(1)歷史上,通信方式為半雙工。現(xiàn)在某些系統(tǒng)提供全雙工管道。
(2)管道只能在具有公共祖先的兩個(gè)進(jìn)程之間使用。通常,一個(gè)管道由一個(gè)進(jìn)程創(chuàng)建,在進(jìn)程調(diào)用fork后,這個(gè)管道就能在父進(jìn)程和子進(jìn)程之間使用了。(FIFO無(wú)此局限)。
???? ???--《Unix環(huán)境高級(jí)編程》
通俗理解: Linux的管道通信,通訊方式正如其名一樣,如同一個(gè)大管道,一端流入,一端流出。半雙工通信方式,即只能一端流入另一端流出;全雙工通信方式,即一端可以流入也可以流出。
PIPEPIPE是一種半雙工管道,其中,fd[1]用來(lái)向管道寫(xiě)入數(shù)據(jù),fd[0]用來(lái)從管道讀出數(shù)據(jù)。若兩個(gè)進(jìn)程需要利用PIPE通信,就要保證一個(gè)進(jìn)程使用fd[0],另一個(gè)進(jìn)程使用fd[1]。
Code:
//參考Linux man手冊(cè)#include <sys/types.h>#include <sys/wait.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>
int main(int argc, char *argv[]){ int pipe_fd[2]; pid_t child_id; char buf;
if (argc != 2) { fprintf(stderr, "Usage: %s <string> ", argv[0]); exit(EXIT_FAILURE); }
if (pipe(pipe_fd) == -1) { perror("pipe"); exit(EXIT_FAILURE); }
child_id = fork(); if (child_id == -1) { perror("fork"); exit(EXIT_FAILURE); }
if (child_id == 0) { /* Child reads from pipe */ close(pipe_fd[1]); /* Close unused write end */
while (read(pipe_fd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1); /*Print to terminal*/
write(STDOUT_FILENO, " ", 1); close(pipe_fd[0]); _exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */ close(pipe_fd[0]); /* Close unused read end */ write(pipe_fd[1], argv[1], strlen(argv[1])); close(pipe_fd[1]); /* Reader will see EOF */ wait(NULL); /* Wait for child */ exit(EXIT_SUCCESS); }}
測(cè)試:
./pipe HelloWorldHelloWorld
小結(jié):
參考man中pipe的使用代碼,大致了解pipe使用方法。即在父進(jìn)程向管道寫(xiě)入終端輸入的 “HelloWorld”,然后在子進(jìn)程讀取管道數(shù)據(jù),并在終端輸出。
在父子進(jìn)程共享區(qū),初始化pipe_fd后,即規(guī)定pipe_fd[0]為讀取端,pipe_fd[1]為寫(xiě)入端。故pipe_fd必須在進(jìn)程共享區(qū)初始化,也就能理解pipe存在開(kāi)篇中第二個(gè)局限性的原因了。
FIFOFIFO有時(shí)也會(huì)被稱為命名管道,未命名的管道(PIPE)只能在兩個(gè)相關(guān)的進(jìn)程間使用,而且這個(gè)兩個(gè)進(jìn)程還要有共同的創(chuàng)建了它們的祖先進(jìn)程。但是,通過(guò)FIFO,不相關(guān)的進(jìn)程也能進(jìn)行數(shù)據(jù)交換。
FIFO的使用方法與讀寫(xiě)文件類似。先創(chuàng)建FIFO文件,再獲取FIFO文件的句柄,然后open、write、read、close。
Code:
fifo寫(xiě)端口代碼實(shí)現(xiàn):
//fifo_write.cpp#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <string.h>#include <iostream>
#define BUFF_SIZE 1024
int main(int argc, char *argv[]){ int ret = 0, fd = 0; char buff[1024] = {0};
if (argc < 2) { fprintf(stderr, "Usage: %s <string> ", argv[0]); exit(EXIT_FAILURE); }
ret = access(argv[1], F_OK); if (ret == -1) { ret = mkfifo(argv[1], 0664); if (ret == 0) { fprintf(stdout, "Create fifo named %s success. ", argv[1]); } else { fprintf(stderr, "Create fifo named %s failed! ", argv[1]); exit(EXIT_FAILURE); } }
fd =open(argv[1], O_RDWR); if (fd == -1) { fprintf(stderr, "Open fifo failed! "); exit(EXIT_FAILURE); }
while(1) { memset(buff, 0, BUFF_SIZE); fprintf(stdout, "input: "); fgets(buff, BUFF_SIZE, stdin);
write(fd, buff, sizeof(buff));
if (strncmp("end", buff, strlen(buff)-1) == 0) { close(fd); break; } }
return 0;}
fifo讀端口代碼實(shí)現(xiàn):
//fifo_read.cpp#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <string.h>#include <iostream>
#define BUFF_SIZE 1024
int main(int argc, char *argv[]){ int ret = 0, fd = 0; char buff[BUFF_SIZE] = {0};
if (argc < 2) { fprintf(stderr, "Usage: %s <string> ", argv[0]); exit(EXIT_FAILURE); }
ret = access(argv[1], F_OK); if (ret == -1) { ret = mkfifo(argv[1], 0664); if (ret == 0) { fprintf(stdout, "Create fifo named %s success. ", argv[1]); } else { fprintf(stderr, "Create fifo named %s failed! ", argv[1]); exit(EXIT_FAILURE); } }
fd =open(argv[1], O_RDWR); if (fd == -1) { fprintf(stderr, "Open fifo failed! "); exit(EXIT_FAILURE); }
while(1) { memset(buff, 0, BUFF_SIZE); read(fd, buff, sizeof(buff));
if (strncmp("end", buff, strlen(buff)-1) == 0) { close(fd); break; }
fprintf(stdout, "%s", buff); }
return 0;}
- 當(dāng)open一個(gè)FIFO時(shí),非阻塞(O_NONBLOCK)會(huì)產(chǎn)生下列影響:
(1) 一般情況下(未指定O_NONBLOCK),只讀open要阻塞到某個(gè)進(jìn)程為寫(xiě)而打開(kāi)這個(gè)FIFO為止。類似的,只寫(xiě)open要阻塞到某個(gè)進(jìn)程為讀而打開(kāi)這個(gè)FIFO為止。
(2)若指定O_NONBLOCK,則只讀open立即返回。但是,若沒(méi)有進(jìn)程為讀而打開(kāi)這個(gè)FIFO,那么只寫(xiě)open則會(huì)返回為-1,將effno設(shè)置為ENXIO。 - 在調(diào)用mkfifo時(shí),會(huì)創(chuàng)建一個(gè)fifo文件。其中第一個(gè)參數(shù)可為絕對(duì)路徑或者相對(duì)路徑。
測(cè)試
總結(jié)對(duì)比以上兩種管道的方式,可得出PIPE與FIFO的大致差異。
- 工作方式。PIPE可稱為“匿名管道”,無(wú)需命名,在具有親屬關(guān)系的進(jìn)程中使用;FIFO又可稱為“有名管道”,在使用過(guò)程中,其會(huì)在系統(tǒng)中創(chuàng)建FIFO類型文件,從而可通過(guò)此文件進(jìn)行不相關(guān)進(jìn)程間的通信。
- 通信方式。PIPE為半雙工通信,即在一次通訊中,數(shù)據(jù)只能在一個(gè)方向上流動(dòng)。FIFO為全雙工通信,在一次通訊中,兩端可以同時(shí)收發(fā)數(shù)據(jù)。
最后
用心感悟,認(rèn)真記錄,寫(xiě)好每一篇文章,分享每一框干貨。愿每一篇文章不負(fù)自己,不負(fù)看客!
?猜你喜歡
? ????詳解 | Linux系統(tǒng)是如何實(shí)現(xiàn)存儲(chǔ)并讀寫(xiě)文件的???
? ????C++打怪 之 vector??
????? 標(biāo)準(zhǔn)字符設(shè)備驅(qū)動(dòng)模板??
更多文章內(nèi)容包括但不限于C/C++、Linux、開(kāi)發(fā)常用神器等,可進(jìn)入開(kāi)源519公眾號(hào)聊天界面回復(fù)“文章目錄” 或者 菜單欄選擇“文章目錄”查看。
本文摘自 :https://blog.51cto.com/u