Linux网络编程 - 在服务器端运用进程间通信之管道(pipe)
一 進(jìn)程間通信的基本概念
1.1 對進(jìn)程間通信的基本理解
進(jìn)程間通信(Inter Process Communication,簡稱 IPC)
進(jìn)程間通信意味著兩個不同進(jìn)程間可以交換數(shù)據(jù),為了實現(xiàn)這一點,操作系統(tǒng)內(nèi)核需要提供兩個進(jìn)程可以同時訪問的內(nèi)存空間,即在內(nèi)核中開辟一塊緩沖區(qū)。整個數(shù)據(jù)交換過程如下圖所示:
圖1? 進(jìn)程間通信從上圖 1-1 可以看出,只要有兩個進(jìn)程可以同時訪問的內(nèi)存空間,就可以通過此空間交換數(shù)據(jù)。但我們知道,進(jìn)程具有完全獨立的內(nèi)存結(jié)構(gòu),就連通過 fork 函數(shù)創(chuàng)建的子進(jìn)程也不會與其父進(jìn)程共享內(nèi)存空間。因此,進(jìn)程間通信只能在操作系統(tǒng)內(nèi)核區(qū)開辟這種共享內(nèi)存緩沖區(qū)。
《拓展》關(guān)于進(jìn)程間通信的機制請參見下面博文鏈接
Linux進(jìn)程之進(jìn)程間通信
二 Linux 的管道(pipe)
2.1 管道的基本概念
管道(pipe) 也稱為匿名管道,是Linux下最常見的進(jìn)程間通信方式之一,它是在兩個進(jìn)程之間實現(xiàn)一個數(shù)據(jù)流通的通道。
基于管道的進(jìn)程間通信結(jié)構(gòu)模型如下圖2所示。
圖2? 基于管道的進(jìn)程間通信模型?????????為了完成進(jìn)程間通信,需要創(chuàng)建管道。管道并非屬于進(jìn)程的資源,而是和套接字一樣,屬于操作系統(tǒng)(也就不是 fork 函數(shù)的復(fù)制對象)。所以,兩個進(jìn)程通過操作系統(tǒng)內(nèi)核提供的內(nèi)存空間進(jìn)行通信。
2.2 管道的特點
Linux 的管道具有以下特點:
- 管道沒有名字,所以也稱為匿名管道。
- 管道是半雙工的通信方式,數(shù)據(jù)只能向一個方向流動;需要雙向通信時,需要建立起兩個管道。(缺點1)
- 管道只能用在父子進(jìn)程或兄弟進(jìn)程之間(即具有親緣關(guān)系的進(jìn)程)。(缺點2)
- 管道單獨構(gòu)成一種獨立的文件系統(tǒng),管道對于管道兩端的進(jìn)程而言,就是一個文件,但它不是普通文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構(gòu)成一種文件系統(tǒng),并且只存在于內(nèi)存中。
- 數(shù)據(jù)的讀出和寫入:一個進(jìn)程向管道中寫入的內(nèi)容被管道另一端的進(jìn)程讀出。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。
- 管道的緩沖區(qū)是有限的(管道只存在于內(nèi)存中,在管道創(chuàng)建時,為緩沖區(qū)分配一個頁面大小)。
- 管道中所傳遞的數(shù)據(jù)是無格式的字節(jié)流,這就要求管道的讀出方和寫入方必須事先約定好數(shù)據(jù)的格式。例如,多少字節(jié)算作一個消息(或命令、記錄等)。
2.3 管道的實現(xiàn)方法
??????? 當(dāng)一個進(jìn)程創(chuàng)建一個管道時,Linux 系統(tǒng)內(nèi)核為使用該管道準(zhǔn)備了兩個文件描述符:一個用于管道的輸入(即進(jìn)程寫操作),也就是在管道中寫入數(shù)據(jù);另一個用于管道的輸出(即進(jìn)程讀操作),也就是從管道中讀出數(shù)據(jù),然后對這兩個文件描述符調(diào)用正常的系統(tǒng)調(diào)用(write、read函數(shù)),內(nèi)核利用這種抽象機制實現(xiàn)了管道這一特殊操作。如下圖 3 所示。
圖3? 管道的結(jié)構(gòu)- ?管道結(jié)構(gòu)的說明
fd0:從管道中讀出數(shù)據(jù)時使用的文件描述符,即管道出口,用于進(jìn)程的讀操作(read),稱為讀管道文件描述符。
fd1:向管道中寫入數(shù)據(jù)時使用的文件描述符,即管道入口,用于進(jìn)程的寫操作(write),稱為寫管道文件描述符。
??????? 如果一個管道只與一個進(jìn)程相聯(lián)系,可以實現(xiàn)進(jìn)程自身內(nèi)部的通信,這個一般用在進(jìn)程內(nèi)線程間的通信(自己遇到過)。
??????? 通常情況下,一個創(chuàng)建管道的進(jìn)程接著就會創(chuàng)建子進(jìn)程,由于子進(jìn)程是復(fù)制父進(jìn)程所有資源創(chuàng)建出的進(jìn)程,因此子進(jìn)程將從父進(jìn)程那里繼承到讀寫管道的文件描述符,這樣父子進(jìn)程間的通信管道就建立起來了。如下圖 4 所示。
圖4? 父進(jìn)程與子進(jìn)程之間的管道《父子進(jìn)程管道半雙工通信說明》
- 父進(jìn)程的 fd[0] = 子進(jìn)程的 f[0],即表示這兩個文件描述符都是標(biāo)識同一個管道的出口端。
- 父進(jìn)程的 fd[1] = 子進(jìn)程的 f[1],即表示這兩個文件描述符都是標(biāo)識同一個管道的入口端。
《父子進(jìn)程數(shù)據(jù)傳輸方向》
父進(jìn)程 —> 子進(jìn)程的數(shù)據(jù)傳輸方向:父進(jìn)程的 fd[1] —> 管道 —> 子進(jìn)程的 fd[0]
子進(jìn)程 —> 父進(jìn)程的數(shù)據(jù)傳輸方向:子進(jìn)程的 fd[1] —> 管道 —> 父進(jìn)程的 fd[0]
??????? 例如,數(shù)據(jù)從父進(jìn)程傳輸給子進(jìn)程時,則父進(jìn)程關(guān)閉讀管道的文件描述符 fd[0],子進(jìn)程關(guān)閉寫管道的文件描述符 fd[1],這樣就建立了從父進(jìn)程到子進(jìn)程的通信管道,如下圖 5 所示。
圖5? 從父進(jìn)程到子進(jìn)程的管道?2.4 管道的讀寫操作規(guī)則
??????? 在建立了一個管道之后即可通過相應(yīng)的文件 I/O 操作函數(shù)(例如 read、write 等)來讀寫管道,以完成數(shù)據(jù)的傳遞過程。
??????? 需要注意的是由于管道的一端已經(jīng)關(guān)閉,在進(jìn)行相應(yīng)的操作時,需要注意以下三個要點:
- 如果從一個寫描述符(fd[1])關(guān)閉的管道中讀取數(shù)據(jù),當(dāng)讀完所有的數(shù)據(jù)后,read 函數(shù)返回0,表明已到達(dá)文件末尾。嚴(yán)格地說,只有當(dāng)沒有數(shù)據(jù)繼續(xù)寫入后,才可以說到達(dá)了完末尾,所以應(yīng)該分清楚到底是暫時沒有數(shù)據(jù)寫入,還是已經(jīng)到達(dá)文件末尾,如果是前者,讀進(jìn)程應(yīng)該等待。若為多進(jìn)程寫、單進(jìn)程讀的情況將更加復(fù)雜。
- 如果向一個讀描述符(fd[0])關(guān)閉的管道中寫數(shù)據(jù),就會產(chǎn)生 SIGPIPE 信號。不管是否忽略這個信號,還是處理它,write 函數(shù)都將返回 -1。
- 常數(shù) PIPE_BUF 規(guī)定了內(nèi)核中管道緩沖的大小,所以在寫管道中要注意一點。一次向管道中寫入 PIPE_BUF 或更少的字節(jié)數(shù)據(jù)時,不會和其他進(jìn)程寫入的內(nèi)容交錯;反之,當(dāng)存在多個寫管道的進(jìn)程時,向其中寫入超過 PIPE_BUF 個字節(jié)數(shù)據(jù)時,將會產(chǎn)生內(nèi)容交錯現(xiàn)象,即覆蓋了管道中的已有數(shù)據(jù)。
三 管道的操作
3.1 管道的創(chuàng)建
Linux 內(nèi)核提供了函數(shù) pipe 用于創(chuàng)建一個管道,對其標(biāo)準(zhǔn)調(diào)用格式說明如下:
- pipe() — 創(chuàng)建一個匿名管道。
【編程實例】使用 pipe 函數(shù)創(chuàng)建管道。在一個進(jìn)程中使用管道的示例。
- pipe.c
- 運行結(jié)果
$ gcc pipe.c -o pipe
$ ./pipe
write data to pipe: This is a test!
read data from pipe: This is a test!
pipe read_fd: 3, write_fd: 4
《注意》在關(guān)閉一個管道時,必須對管道的兩端都執(zhí)行 close 操作,也就是說要對管道的兩個文件描述符都進(jìn)行 close 操作。
3.2 通過管道實現(xiàn)進(jìn)程間通信
????????當(dāng)父進(jìn)程調(diào)用 pipe 函數(shù)時將創(chuàng)建管道,同時獲取對應(yīng)于管道出入口兩端的文件描述符,此時父進(jìn)程可以讀寫同一管道,也就是本示例程序中那樣。但父進(jìn)程的目的通常是與子進(jìn)程進(jìn)行數(shù)據(jù)交換,因此需要將管道入口或出口中的其中一個文件描述符傳遞給子進(jìn)程。如何傳遞呢?答案就是調(diào)用 fork 函數(shù)。
- 在父子進(jìn)程中使用管道的詳細(xì)步驟
1、在父進(jìn)程中調(diào)用 pipe 函數(shù)創(chuàng)建一個管道。
2、在父進(jìn)程中調(diào)用 fork 函數(shù)創(chuàng)建一個子進(jìn)程。
3、在父進(jìn)程中關(guān)閉不使用的管道一端的文件描述符,然后調(diào)用對應(yīng)的寫操作函數(shù),例如 write,將對應(yīng)的數(shù)據(jù)寫入管道。
4、在子進(jìn)程中關(guān)閉不使用的管道一端的文件描述符,然后調(diào)用對應(yīng)的讀操作函數(shù),例如 read,將對應(yīng)的數(shù)據(jù)從管道中讀出。
5、在父子進(jìn)程中,調(diào)用 close 函數(shù),關(guān)閉管道的文件描述符。
【編程實例】在父子進(jìn)程中使用管道。在父進(jìn)程中創(chuàng)建一個管道,并調(diào)用 fork 函數(shù)創(chuàng)建一個子進(jìn)程,父進(jìn)程將一行字符串?dāng)?shù)據(jù)寫入管道,在子進(jìn)程中,從管道讀出這個字符串并打印出來。
- pipe_fatherson.c
- 運行結(jié)果
$ gcc pipe_fatherson.c -o pipe_fatherson
[wxm@centos7 pipe]$ ./pipe_fatherson
Parent Proc, fds[0]=3, fds[1]=4
Child Proc, fds[0]=3, fds[1]=4
Who are you?
Who are you?
《代碼說明》
- 第14行:在父進(jìn)程中調(diào)用 pipe 函數(shù)創(chuàng)建管道,fds 數(shù)組中保存用于讀寫 I/O 的文件描述符。
- 第18行:接著調(diào)用 fork 函數(shù)。子進(jìn)程將同時擁有通過第14行 pipe 函數(shù)調(diào)用獲取的2個文件描述符,從上面的運行結(jié)果可以驗證這一點。注意!復(fù)制的并非管道,而是用于管道 I/O 的文件描述符。至此,父子進(jìn)程同時擁有管道 I/O 的文件描述符。
- 第27、33行:父進(jìn)程通過第27行代碼,向管道寫入字符串;子進(jìn)程通過第33行代碼,從管道接收字符串。
- 第36、39行:第36行代碼,子進(jìn)程結(jié)束運行前,關(guān)閉管道的讀出端文件描述符;第39行代碼,父進(jìn)程(也是主進(jìn)程)結(jié)束運行前,關(guān)閉管道的寫入端文件描述符。
- 在兄弟進(jìn)程中使用管道
??????? 在兄弟進(jìn)程中使用管道進(jìn)行數(shù)據(jù)通信的方法和在父子進(jìn)程中類似,只是將對管道進(jìn)行操作的兩個進(jìn)程更換為兄弟進(jìn)程即可,在父進(jìn)程中則關(guān)閉該管道的 I/O 文件描述符。
【編程實例】值兄弟進(jìn)程中使用管道的應(yīng)用實例。首先在主進(jìn)程(也就是父進(jìn)程)中創(chuàng)建一個管道和兩個子進(jìn)程,然后在第1個子進(jìn)程中將一個字符串通過管道發(fā)送給第2個子進(jìn)程,第2個子進(jìn)程從管道中讀出數(shù)據(jù),然后將該數(shù)據(jù)輸出到屏幕上。
- pipe_brother.c
- 運行結(jié)果
$ gcc pipe_brother.c -o pipe_brother
[wxm@centos7 pipe]$ ./pipe_brother
Parent Proc, fds[0]=3, fds[1]=4
Child1 Proc, fds[0]=3, fds[1]=4
Child2 Proc, fds[0]=3, fds[1]=4
Hello,I`m your brother!
Hello,I`m your brother!
Child1 proc eixt, pid=4679
Child proc send 1
Child2 proc eixt, pid=4680
Child proc send 2
《代碼說明》
- 第54、58、62行:在父進(jìn)程中調(diào)用 waitpid 函數(shù),等待子進(jìn)程的終止,如果沒有終止的子進(jìn)程也不會進(jìn)入阻塞狀態(tài),而是返回0。當(dāng)子進(jìn)程1結(jié)束運行時,函數(shù)返回該子進(jìn)程的進(jìn)程ID,執(zhí)行第58行的代碼;同理,當(dāng)子進(jìn)程2結(jié)束運行時,函數(shù)返回該子進(jìn)程的進(jìn)程ID,執(zhí)行第62行的代碼。
3.3 通過管道實現(xiàn)進(jìn)程間雙向通信
下面創(chuàng)建2個進(jìn)程和1個管道進(jìn)行雙向數(shù)據(jù)交換的示例,其通信方式如下圖6所示。
圖6? 管道雙向通信模型1?從圖6可以看出,通過一個管道可以進(jìn)行雙向數(shù)據(jù)通信。但采用這種模型時需格外注意。先給出示例,稍后再分析討論。
- pipe_duplex.c
- 運行結(jié)果
$ gcc pipe_duplex.c -o pipe_duplex
$ ./pipe_duplex
Parent porc output: Who are you?
Child proc output: Thank you for your message
??????? 運行結(jié)果和我們預(yù)想的一樣:子進(jìn)程向管道中寫入字符串 str1,父進(jìn)程從管道中讀出該字符串;父進(jìn)程向管道中寫入字符串 str2,子進(jìn)程從管道中讀出該字符串。如果我們將第 27 行的代碼注釋掉,運行結(jié)果會是怎樣呢?
$ ./pipe_duplex
Child proc output: Who are you?
從上面的運行結(jié)果和進(jìn)程狀態(tài)可以看出,進(jìn)程 pipe_duplex 陷入了 死鎖狀態(tài)(<defunct>),產(chǎn)生的原因是什么呢?
“向管道中傳遞數(shù)據(jù)時,先讀的進(jìn)程會把管道中的數(shù)據(jù)取走。”
????????數(shù)據(jù)進(jìn)入管道后成為無主數(shù)據(jù)。也就是通過 read 函數(shù)先讀取數(shù)據(jù)的進(jìn)程將得到數(shù)據(jù),即使該進(jìn)程將數(shù)據(jù)傳到了管道。因此,注釋掉第 27 行代碼將產(chǎn)生問題。在第 28 行,子進(jìn)程將讀回自己在第 26 行向管道發(fā)送的數(shù)據(jù)。結(jié)果,父進(jìn)程調(diào)用 read 函數(shù)后將無限期等待數(shù)據(jù)進(jìn)入管道,導(dǎo)致進(jìn)程陷入死鎖。
??????? 從上述示例中可以看到,只用一個管道進(jìn)行進(jìn)程間的雙向通信并非易事。為了實現(xiàn)這一點,程序需要預(yù)測并控制運行流程,這在每種系統(tǒng)中都不同,可以視為不可能完成的任務(wù)。既然如此,該如何進(jìn)行雙向通信呢?
“創(chuàng)建兩個管道。”
??????? 非常簡單,一個管道無法完成雙向通信任務(wù),因此需要創(chuàng)建兩個管道,各自負(fù)責(zé)不同的數(shù)據(jù)流動方向即可。其過程如下圖 7 所示。
圖7? 雙向通信模型2???????? 由上圖 7 可知,使用兩個管道可以避免程序流程的不可預(yù)測或不可控制因素。下面采用上述模型改進(jìn) pipe_duplex.c 程序。
- pipe_duplex2.c
- 運行結(jié)果
$ gcc pipe_duplex2.c -o pipe_duplex2
$ ./pipe_duplex2
Parent porc output: Who are you?
Child proc output: Thank you for your message
- 程序說明
1、子進(jìn)程 ——> 父進(jìn)程:通過數(shù)組 fds1 指向的管道1進(jìn)行數(shù)據(jù)交互。
2、父進(jìn)程 ——> 子進(jìn)程:通過數(shù)組 fds2 指向的管道2進(jìn)行數(shù)據(jù)交互。
四 在網(wǎng)絡(luò)編程中運用管道實現(xiàn)進(jìn)程間通信
上一節(jié)我們學(xué)習(xí)了基于管道的進(jìn)程間通信方法,接下來將其運用到網(wǎng)絡(luò)編程代碼中。
4.1 保存消息的回聲服務(wù)器端
下面我們擴展上一篇博文中的服務(wù)器端程序 echo_mpserv.c,添加如下功能:
“將回聲客戶端傳輸?shù)淖址葱虮4娴轿募小?/span>”
????????我們將這個功能任務(wù)委托給另外的進(jìn)程。換言之,另行創(chuàng)建進(jìn)程,從向客戶端提供服務(wù)的進(jìn)程讀取字符串信息。這就涉及到進(jìn)程間通信的問題。為此,我們可以使用上面講過的管道來實現(xiàn)進(jìn)程間通信過程。下面給出示例程序。該示例可以與任意回聲客戶端配合運行,但我們將使用前一篇博文中介紹過的 echo_mpclient.c。
【提示】服務(wù)器端程序 echo_mpserv.c 和 客戶端程序 echo_mpclient.c,請參見下面的博文鏈接獲取。
Linux網(wǎng)絡(luò)編程 - 多進(jìn)程服務(wù)器端(2)
- echo_storeserv.c
- 代碼說明
- 第55、56行:第55行創(chuàng)建管道,第56行創(chuàng)建負(fù)責(zé)保存數(shù)據(jù)到文件中的子進(jìn)程。
- 第57~72行:這部分代碼是第56行創(chuàng)建的子進(jìn)程運行區(qū)域。該代碼執(zhí)行區(qū)域從管道出口端 fds[0] 讀取數(shù)據(jù)并保存到文件中。另外,上述服務(wù)器端并不終止運行,而是不斷向客戶端提供服務(wù)。因此,數(shù)據(jù)在文件中累計到一定程度即關(guān)閉文件,該過程通過第63行的 for 循環(huán)完成。
- 第99行:第87行通過 fork 函數(shù)創(chuàng)建的子進(jìn)程將復(fù)制第55行創(chuàng)建的管道的文件描述符數(shù)組 fds。因此,可以通過管道入口端 fds[1] 向管道傳遞字符串?dāng)?shù)據(jù)。
- 運行結(jié)果
- 服務(wù)器端:echo_storeserv.c
$ gcc echo_storeserv.c -o storeserv
[wxm@centos7 echo_tcp]$ ./storeserv 9190
New client connected from address[127.0.0.1:60534], conn_id=6
New child proc ID: 5589
New client connected from address[127.0.0.1:60536], conn_id=6
New child proc ID: 5592
remove proc id: 5586
client[127.0.0.1:60534] disconnected, conn_id=6
remove proc id: 5589
client[127.0.0.1:60536] disconnected, conn_id=6
remove proc id: 5592
- 客戶端1:echo_mpclient.c
$ ./mpclient 127.0.0.1 9190
Connected...........
One
Message from server: One
Three
Message from server: Three
Five
Message from server: Five
Seven
Message from server: Seven
Nine
Message from server: Nine
Q
[wxm@centos7 echo_tcp]$
- 客戶端2:echo_mpclient.c
$ ./mpclient 127.0.0.1 9190
Connected...........
Two
Message from server: Two
Four
Message from server: Four
Six
Message from server: Six
Eight
Message from server: Eight
Ten
Message from server: Ten
Q
[wxm@centos7 echo_tcp]$
- 查看 echomsg.txt 文件內(nèi)容
[wxm@centos7 echo_tcp]$ cat echomsg.txt
One
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
[wxm@centos7 echo_tcp]$
《提示》觀察示例 echo_storeserv.c 后,可以發(fā)現(xiàn)在 main 函數(shù)中,代碼內(nèi)容太長,有點影響代碼閱讀和理解。我們其實可以嘗試針對一部分功能以函數(shù)為模塊單位重構(gòu)代碼,有興趣的話,可以試一試,讓代碼結(jié)構(gòu)更加緊湊、美觀。
五 多進(jìn)程并發(fā)服務(wù)器端總結(jié)
??????? 前面我們已經(jīng)實現(xiàn)了多進(jìn)程并發(fā)服務(wù)器端模型,但它只是并發(fā)服務(wù)器模型中的其中之一。如果我們有如下的想法:
“我想利用進(jìn)程和管道編寫聊天室程序,使多個客戶端進(jìn)行對話,應(yīng)該從哪著手呢?”
??????? 若想僅用進(jìn)程和管道構(gòu)建具有復(fù)雜功能的服務(wù)器端,程序員需要具備熟練的編程技術(shù)和經(jīng)驗。因此,初學(xué)者應(yīng)用該模型擴展程序并非易事,希望大家不要過于拘泥。以后要說明的另外兩種并發(fā)服務(wù)器端模型在功能上更加強大,同時更容易實現(xiàn)我們的想法。
??????? 在實際網(wǎng)絡(luò)編程開發(fā)項目中,幾乎不會用到多進(jìn)程并發(fā)服務(wù)器端模型,因為它并不是一種高效的并發(fā)服務(wù)器模型,不適合實際應(yīng)用場景。即使我們在實際開發(fā)項目中不會利用多進(jìn)程模型構(gòu)建服務(wù)器端,但這些內(nèi)容我們還是有必要學(xué)習(xí)和掌握的。
????????最后跟大家分享一句他人的一條學(xué)習(xí)編程經(jīng)驗之談:“即使開始時只需學(xué)習(xí)必要部分,但最后也會需要掌握所有的內(nèi)容。”
《提示》另外兩種比較高效的并發(fā)服務(wù)器端模型為:I/O 復(fù)用、多線程服務(wù)器端。
六 習(xí)題
1、什么是進(jìn)程間通信?分別從概念上和內(nèi)存的角度進(jìn)行說明。
答:從概念上講,進(jìn)程間通信是指兩個進(jìn)程之間交換數(shù)據(jù)的過程。從內(nèi)存的角度上講,就是兩個進(jìn)程共享的內(nèi)存,通過這個共享的內(nèi)存區(qū)域,可以進(jìn)行數(shù)據(jù)交換,而這個共享的內(nèi)存區(qū)域是在操作系統(tǒng)內(nèi)核區(qū)中開辟的。
2、進(jìn)程間通信需要特殊的IPC機制,這是由操作系統(tǒng)提供的。進(jìn)程間通信時為何需要操作系統(tǒng)的幫助?
答:兩個進(jìn)程之間要想交換數(shù)據(jù),需要一塊共享的內(nèi)存,但由于每個進(jìn)程的地址空間都是相互獨立的,因此需要操作系統(tǒng)的幫助。也就是說,兩個進(jìn)程共享的內(nèi)存空間必須由操作系統(tǒng)來提供。
3、“管道”是典型的IPC技術(shù)。關(guān)于管道,請回答如下問題。
a. 管道是進(jìn)程間交換數(shù)據(jù)的路徑。如何創(chuàng)建該路徑? 由誰創(chuàng)建?
b. 為了完成進(jìn)程間通信,2個進(jìn)程需同時連接管道。那2個進(jìn)程如何連接到同一管道?
c. 管道允許進(jìn)行2個進(jìn)程間的雙向通信。雙向通信中需要注意哪些內(nèi)容?
- a:在父進(jìn)程(或主進(jìn)程)中調(diào)用 pipe 函數(shù)創(chuàng)建管道。實際管道的創(chuàng)建主體是操作系統(tǒng),管道不是屬于進(jìn)程的資源,而是屬于操作系統(tǒng)的資源。
- b:pipe 函數(shù)通過傳入?yún)?shù)返回管道的出入口兩端的文件描述符。當(dāng)調(diào)用 fork 函數(shù)創(chuàng)建子進(jìn)程時,這兩個文件描述符會被復(fù)制到子進(jìn)程中,因此,父子進(jìn)程可以同時訪問同一管道。
- c:數(shù)據(jù)進(jìn)入管道后就變成了無主數(shù)據(jù)。因此,只要有數(shù)據(jù)流入管道,任何進(jìn)程都可以讀取數(shù)據(jù)。因此,要合理安排管道中數(shù)據(jù)的寫入和讀出順序。
4、編寫示例復(fù)習(xí)IPC技術(shù),使2個進(jìn)程相互交換3次字符串。當(dāng)然,這兩個進(jìn)程應(yīng)具有父子關(guān)系,各位可指定任意字符串。
答:問題剖析:兩個父子進(jìn)程要互相交換數(shù)據(jù),可以通過管道方式實現(xiàn)進(jìn)程間通信,而通過創(chuàng)建兩個管道可以實現(xiàn)進(jìn)程間的雙向通信。我們假設(shè)是子進(jìn)程先向父進(jìn)程發(fā)送消息,然后父進(jìn)程回復(fù)消息,如此往復(fù)3次后結(jié)束運行。
- pipe_procipc.c
- 運行結(jié)果
$ gcc pipe_procipc.c -o pipe_procipc
[wxm@centos7 pipe]$ ./pipe_procipc
Child send message: Hi,I`m child proc
Parent recv message: Hi,I`m child proc
Parent resp message: Hi,I`m parent proc
Child recv message: Hi,I`m parent proc
Child send message: Nice to meet you
Parent recv message: Nice to meet you
Parent resp message: Nice to meet you, too
Child recv message: Nice to meet you, too
Child send message: Good bye!
Parent recv message: Good bye!
Parent resp message: Bye bye!
Child recv message: Bye bye!
[wxm@centos7 pipe]$
參考
《TCP-IP網(wǎng)絡(luò)編程(尹圣雨)》第11章 - 進(jìn)程間通信
《Linux C編程從基礎(chǔ)到實踐(程國鋼、張玉蘭)》第9章 - Linux的進(jìn)程同步機制——管道和IPC
《TCP/IP網(wǎng)絡(luò)編程》課后練習(xí)答案第一部分11~14章 尹圣雨
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程 - 在服务器端运用进程间通信之管道(pipe)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果的傲慢与堕落,从iPhone XS的
- 下一篇: linux pipe命令,Linux入门