进程间的通信方式(二):管道Pipe和命令管道FIFO
生活随笔
收集整理的這篇文章主要介紹了
进程间的通信方式(二):管道Pipe和命令管道FIFO
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1.概述
管道是最初的Unix?IPC通信,可追溯到1973年的Unix第三版。盡管對于許多操作來說很有用,但它們的根本局限于沒有名字,只能由親緣關(guān)系的進(jìn)程使用。這一點(diǎn)隨著FIFO的加入System?III?Unix中得以改正。FIFO有時(shí)候稱為命令管道(named?pipe)。管道和FIFO都是使用通常的read和write函數(shù)訪問的。 從技術(shù)上講,自從可以在進(jìn)程間傳遞描述符后,管道也能用于無親緣關(guān)系的進(jìn)程間。然而通常的說,管道通常用于具有共同祖先的進(jìn)程間通信。1.1管道的特點(diǎn)
(1)只能用于具有共同祖先的進(jìn)程(具有親緣關(guān)系的進(jìn)程)之間通信,當(dāng)然除了命名管道,命名管道有一個(gè)路徑名與之關(guān)聯(lián),命名管道允許無親緣關(guān)系的進(jìn)程訪問同一個(gè)FIFO;通常,一個(gè)管道由一個(gè)進(jìn)程創(chuàng)建,然后該進(jìn)程調(diào)用fork,此后,父子進(jìn)程之間就可應(yīng)用該管道? (2)管道的進(jìn)程間通信是基于字節(jié)流的? (3)管道是基于文件形式的,自帶同步互斥機(jī)制,并且只能進(jìn)行單向傳輸? (4)一般而言,進(jìn)程退出,管道釋放,所以管道的生命周期是隨進(jìn)程的2.管道
所有樣式的Unix提供管道。它由pipe函數(shù)創(chuàng)建,提供一個(gè)單路(單向)數(shù)據(jù)流。 調(diào)用pipe函數(shù)時(shí)在內(nèi)核中開辟一塊緩沖區(qū)(稱為管道)用于通信,它有一個(gè)讀端一個(gè)寫端,然后通過pipefd參數(shù)傳出給用戶程序兩個(gè)文件描述符,pipefd[0]指向管道的讀端,fpipefd[1]指向管道的寫端。所以管道在用戶程序看起來就像一個(gè)打開的文件,通過read(filedes[0]);或者write(filedes[1]);向這個(gè)文件讀寫數(shù)據(jù)其實(shí)是在讀寫內(nèi)核緩沖區(qū)。pipe函數(shù)調(diào)用成功返回0,調(diào)用失敗返回-1。3.管道實(shí)現(xiàn)進(jìn)程間通信
開辟了管道之后如何實(shí)現(xiàn)兩個(gè)進(jìn)程間的通信呢?比如可以按下面的步驟通信。3.1單個(gè)進(jìn)程中的管道
? ? 父進(jìn)程調(diào)用pipe函數(shù)創(chuàng)建管道,得到兩個(gè)文件描述符fd[0]、fd[1]指向管道的讀端和寫端。父進(jìn)程中可以通過管道一方寫入數(shù)據(jù),父進(jìn)程也可以通過管道的另一方讀取數(shù)據(jù),上圖很好的說明了當(dāng)個(gè)進(jìn)程中的管道通信方式。3.2?父進(jìn)程fork出子進(jìn)程
管道的經(jīng)典用途是以下方式為兩個(gè)不同進(jìn)程(一個(gè)是父進(jìn)程一個(gè)是子進(jìn)程)提供進(jìn)程間通信手段。首先,由一個(gè)進(jìn)程)(它將成為父進(jìn)程)創(chuàng)建一個(gè)管道后調(diào)用fork派生一個(gè)自身的副本,如下圖所示: 父進(jìn)程fork出子進(jìn)程,?進(jìn)程也有兩個(gè)?件描述符指向同?管道。那么這樣在父進(jìn)程和子進(jìn)程中都有兩個(gè)文件描述符分別指向管道的寫端和讀端。父進(jìn)程和子進(jìn)程都能對管道進(jìn)行讀寫操作。3.3?父進(jìn)程關(guān)閉fd[0]?子進(jìn)程關(guān)閉fd[1]
父進(jìn)程關(guān)閉在這個(gè)管道的讀出端fd[0],子進(jìn)程關(guān)閉這個(gè)管道的寫入端fd[1]。這就在父進(jìn)程和子進(jìn)程之間提供了一個(gè)單向數(shù)據(jù)流。父進(jìn)程可以向管道中寫入數(shù)據(jù),子進(jìn)程將管道中的數(shù)據(jù)讀出。由于管道是利用環(huán)形隊(duì)列實(shí)現(xiàn)的,數(shù)據(jù)從寫端流入管道,從讀端流出,這樣就實(shí)現(xiàn)了進(jìn)程間通信。3.4?代碼實(shí)現(xiàn)管道通信
子進(jìn)向父進(jìn)程發(fā)送數(shù)據(jù) #include <stdio.h> #include <unistd.h> #include <string.h>int main() {int fd[2];if (pipe(fd)){perror("pipe");return 1;}// 實(shí)現(xiàn)父進(jìn)程寫,子進(jìn)程讀pid_t id = fork();if (id < 0){perror("fork");return 2;}else if (id == 0) // child{close(fd[1]);char buf[128];int cnt = 0;while (cnt++ < 5){ssize_t _s = read(fd[0], buf, sizeof(buf));if (_s > 0){buf[_s] = '\0';;printf("father say to child: %s\n", buf);}else if (_s == 0){printf("father close write");break;}else{perror("read");break;}}close(fd[0]);}else // father{close(fd[0]);char * msg = "hello world";int cnt = 0;while (cnt++ < 5){write(fd[1], msg, strlen(msg));sleep(1);}close(fd[1]);}return 0; }3.命名管道(FIFO)
管道沒有名字,因此它們的最大的劣勢就是只能用于有一個(gè)共同祖先進(jìn)程的各個(gè)進(jìn)程之間的通信。我們無法在無親緣關(guān)系的兩個(gè)進(jìn)程之間創(chuàng)建一個(gè)管道并將它作為IPC通道(不考慮描述符的傳遞)。管道的缺點(diǎn)就是只能在有親緣關(guān)系的進(jìn)程間進(jìn)行通信,針對這個(gè)缺陷,又提出來了命名管道(FIFO)的概念。FIFO不同于管道之處在于它提供一個(gè)路徑名與之關(guān)聯(lián),以FIFO的文件形式存儲(chǔ)于文件系統(tǒng)中。命名管道是一個(gè)設(shè)備文件,因此,即使進(jìn)程與創(chuàng)建FIFO的進(jìn)程不存在親緣關(guān)系,只要可以訪問該路徑,就能夠通過FIFO相互通信。 FIFO指的就是先進(jìn)先出(first?in,first on),Unix中的FIFO類似管道。他是一個(gè)單向(半雙工)的數(shù)據(jù)流。不同于管道的是,每個(gè)FIFO有一個(gè)路徑與之相連,從而允許無親緣關(guān)系之間的進(jìn)程訪問同一個(gè)FIFO。FIFO也稱為有名管道(named?pipe)。 FIFO通過mkfifo函數(shù)創(chuàng)建 其中pathname是一個(gè)普通的Unix路徑名,它是該FIFO的名字。 mode參數(shù)指的是文件權(quán)限位,類似于open的第二個(gè)參數(shù)。mkfifo命令也能創(chuàng)建FIOF。可以從shell腳本或者命令行中使用它。在創(chuàng)建一個(gè)FIFO后,它必須或者打開來讀或者來寫,所用的可以是open函數(shù),也可以是某個(gè)標(biāo)準(zhǔn)I/O打開函數(shù),列如fopen。FIOFU不能打開來既可以讀又可以寫,因?yàn)樗前腚p工的。對管道或者FIFO的write總是向末尾添加數(shù)據(jù),對他們的read總是從開頭添加數(shù)據(jù)。如果對管道或者FIFO調(diào)用lseek,這就返回錯(cuò)誤。3.1FIFO兩個(gè)屬性
1、FIFO是一個(gè)設(shè)備文件,在文件系統(tǒng)中以文件名的形式存在,因此即使進(jìn)程與創(chuàng)建FIFO的進(jìn)程不存在血緣關(guān)系也依然可以通信,前提是可以訪問該路徑。? 2、FIFO(first input first output)總是遵循先進(jìn)先出的原則,即第一個(gè)進(jìn)來的數(shù)據(jù)會(huì)第一個(gè)被讀走。3.2?代碼實(shí)現(xiàn)命令管道通信
server向命令管道中發(fā)送數(shù)據(jù) #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h>int main() {// 創(chuàng)建管道時(shí)需要在mode參數(shù)位置傳S_IFIFO,表明創(chuàng)建的是命名管道int ret = mkfifo("./.fifo", S_IFIFO | 0644); if (ret < 0){perror("mkfifo");return 1;}int fd = open("./.fifo", O_WRONLY);if (fd < 0){perror("open");return 2;}int cnt = 0;char *msg = "hello world";while (cnt++ < 5){write(fd, msg, strlen(msg));sleep(1);}close(fd);return 0; } client向命令管道中接收數(shù)據(jù)#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h>int main() {int fd = open("./.fifo", O_RDONLY);if (fd < 0){perror("open");return 2;}int cnt = 0;char buf[128];while (cnt++ < 5){ssize_t _s = read(fd, buf, sizeof(buf) - 1);if (_s > 0){buf[_s] = '\0';;printf("server say to client: %s\n", buf);}else if (_s == 0){printf("server close write\n");break;}else{perror("read");}sleep(1);}close(fd);return 0; }4. 命令管道和管道的區(qū)別
命名管道創(chuàng)建完成后就可以使用,其使用方法與管道一樣;?
區(qū)別在于:?
(1)命名管道使用之前需要使用open()打開。這是因?yàn)?#xff1a;命名管道是設(shè)備文件,它是存儲(chǔ)在硬盤上的,而管道是存在內(nèi)存中的特殊文件。?
(2)但是需要注意的是,命名管道調(diào)用open()打開有可能會(huì)阻塞,但是如果以讀寫方式(O_RDWR)打開則一定不會(huì)阻塞;?
(3)命名管道以只讀(O_RDONLY)方式打開時(shí),調(diào)用open()的函數(shù)會(huì)被阻塞直到有數(shù)據(jù)可讀;?
(4)命名管道如果以只寫方式(O_WRONLY)打開時(shí)同樣也會(huì)被阻塞,知道有以讀方式打開該管道。
總結(jié)
以上是生活随笔為你收集整理的进程间的通信方式(二):管道Pipe和命令管道FIFO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程间的通信方式:简介
- 下一篇: 进程间通信的方式(三):消息队列