Linux管道揭秘之匿名管道連接進(jìn)程世界的方法
1.什么是管道 ?
管道(Pipe)是一種常見的進(jìn)程間通信(IPC,Inter-Process Communication)機(jī)制,在 Unix/Linux 系統(tǒng)中尤其重要。它允許一個進(jìn)程的輸出直接作為另一個進(jìn)程的輸入,而不需要使用中間文件。管道通常用于將多個命令連接起來,讓它們像流水線一樣處理數(shù)據(jù)。
管道在 Unix/Linux 系統(tǒng)中提供了一種簡便的機(jī)制,允許數(shù)據(jù)在不同進(jìn)程之間傳遞。它提供了一個緩沖區(qū),數(shù)據(jù)寫入管道的一端(寫端),然后可以從另一端(讀端)讀取。管道的本質(zhì)是一種半雙工的通信機(jī)制,即數(shù)據(jù)只能沿一個方向流動。
提問:有沒有一些直觀的管道的利用?
當(dāng)然。其實早在Linux的指令學(xué)習(xí)中,我們就已經(jīng)接觸到了管道。就是這個符號|
。
ubuntu@VM-20-9-ubuntu:~/pipeTest$ ls -l total 24 -rwxrwxr-x 1 ubuntu ubuntu 16576 Nov 5 11:41 a.out -rw-rw-r-- 1 ubuntu ubuntu 1285 Nov 5 11:40 pipeTest1.c ubuntu@VM-20-9-ubuntu:~/pipeTest$ ls -l|grep "pipeTest1.c" -rw-rw-r-- 1 ubuntu ubuntu 1285 Nov 5 11:40 pipeTest1.c ubuntu@VM-20-9-ubuntu:~/pipeTest$
這就是一個管道的簡單使用,我們都知道,在大部分Linux的指令都是一個可執(zhí)行文件,運行起來就是一個進(jìn)程。ls -l
的作用就是顯示當(dāng)前目錄文件的信息,現(xiàn)在我們通過|
將這個顯示的信息通過管道傳遞給grep,不就實現(xiàn)了兩個進(jìn)程間的相互通信了嘛。這就是管道的核心作用:實現(xiàn)進(jìn)程間的通信,高效傳遞數(shù)據(jù),避免了使用臨時文件的麻煩.
2. 管道的類型
管道存在兩種類型:
- 匿名管道,用于父子進(jìn)程或者兄弟進(jìn)程間的數(shù)據(jù)傳遞,沒有名字,僅限具有親緣關(guān)系的進(jìn)程。
- 命名進(jìn)程,具有文件名,可以在不相干的進(jìn)程間使用。
2.1 匿名管道
匿名管道通過pipe()
創(chuàng)建。
2.1.1 介紹pipe()
#include <unistd.h> int pipe(int pipefd[2]);
pipefd
:是一個數(shù)組,它包含兩個元素,分別是管道的讀端和寫端的文件描述符。
pipefd[0]
:讀端(用于讀取數(shù)據(jù))。pipefd[1
]:寫端(用于寫入數(shù)據(jù))。pipe()
創(chuàng)建一個管道,并將兩個文件描述符存儲在pipefd
數(shù)組中。- 管道的數(shù)據(jù)流是單向的:數(shù)據(jù)從寫端流向讀端。
- 關(guān)于返回值:
- 成功:返回0.
- 失?。悍祷?1.
使用pepe()
的基本流程:
- 創(chuàng)建管道:調(diào)用
pipe()
函數(shù)。 - 使用
fork()
創(chuàng)建一個子進(jìn)程。 - 在父進(jìn)程關(guān)閉寫端,使用讀端讀取數(shù)據(jù)。
- 在子進(jìn)程中關(guān)閉讀端,使用寫端將數(shù)據(jù)傳輸給父進(jìn)程。
2.1.2 pipe()簡單示例:父子進(jìn)程通過管道通信
//本代碼用來測試子進(jìn)程提供匿名管道將信息傳遞給父進(jìn)程 24/11/5 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #define SIZE 1024 void writer(int wfd) { char buf[SIZE]; const char* str = "hello father,i am child"; int count = 1; pid_t id = getpid(); while(true) { //格式化輸入 snprintf(buf,sizeof(buf)-1,"message:%s,pid:%d,times:%d",str,id,count); write(wfd,buf,strlen(buf)); count+=1; sleep(1); } } void reader(int rfd) { char buf[SIZE]; while(true) { ssize_t n = read(rfd,buf,sizeof(buf)-1); if(n == -1) { perror("read"); return; } printf("%s\n",buf); } } int main() { //文件標(biāo)識符 int fd[2]; if(pipe(fd) < 0) { //error perror("pipe error"); return 1; } pid_t id = fork(); if(id<0) { perror("fork error"); return 1; } else if(id == 0) { //child //關(guān)閉讀端 close(fd[0]); writer(fd[1]); exit(1); } //father close(fd[1]); reader(fd[0]); wait(NULL); return 0; }
運行結(jié)果:
如此我們我們便實現(xiàn)了父子間的管道通信。pipe()
是一個非常重要的系統(tǒng)調(diào)用,它為進(jìn)程間通信提供了一個簡單而高效的機(jī)制。通過管道,多個進(jìn)程可以協(xié)作完成任務(wù),并且避免了中間文件的使用。在父子進(jìn)程之間的通信,或在處理大量數(shù)據(jù)時,管道通常是最常用的 IPC 方式之一。
2.1.3 管道的4種情況與5種特性
4種情況:
- 管道內(nèi)部沒有數(shù)據(jù)時且子進(jìn)程不關(guān)閉自己的寫端文件fd,讀端(父)就會堵塞等待,直到pipe有數(shù)據(jù),
- 管道內(nèi)部被寫滿且父進(jìn)程(讀端)不關(guān)閉自己的fd,寫端寫滿后,就會堵塞等待。
- 對于寫端而言:不寫了且關(guān)閉了pipe,讀端會將pipe中的數(shù)據(jù)讀完,最后就會讀到返回值為0,表示讀結(jié)束,類似讀到了文件的結(jié)尾。
- 讀端不讀且關(guān)閉,寫再寫,OS會直接終結(jié)寫入的進(jìn)程(子進(jìn)程)通過信號13)SIGPIPE來殺死進(jìn)程。
- 5種特性:
- 自帶同步機(jī)制。
- 血緣關(guān)系進(jìn)行通信,常見于父子進(jìn)程。
- pipe是面向字節(jié)流的。
- 父子進(jìn)程退出,管道自動釋放,文件的生命周期是跟隨進(jìn)程的。
- 管道只能單向通信,半雙工的一種特殊情況。
2.1.4 匿名管道原理
通過父子進(jìn)程繼承關(guān)系,再將文件描述符關(guān)閉,實現(xiàn)一端寫,一端讀就是匿名管道.
創(chuàng)建匿名管道的步驟:
父進(jìn)程以讀寫的方式打開,文件。父進(jìn)程fork創(chuàng)建子進(jìn)程,子進(jìn)程會拷貝一份PCB結(jié)構(gòu),PCB中會包含files_struct結(jié)構(gòu),files_struct中有一個指向struct file(文件)的指針數(shù)組,而文件描述符就是這個數(shù)組的下標(biāo)。
拷貝完成后,子進(jìn)程也就存在了指向struct file的對應(yīng)文件描述符。
又因為,struct file是獨屬于的文件的,和進(jìn)程沒有關(guān)系,也就不用拷貝,也就是說此時父子進(jìn)程同時指向了一塊公共區(qū)域struct file(不同進(jìn)程看見同一份資源)。
write是系統(tǒng)調(diào)用接口,會將數(shù)據(jù)放在內(nèi)核緩沖區(qū),底層會定期刷新緩沖區(qū)將內(nèi)容寫入磁盤。
匿名管道是一個半雙工的通信機(jī)制,也就是說,數(shù)據(jù)只能沿一個方向流動,為了實現(xiàn)半雙工的通信方式,父子進(jìn)程需要關(guān)閉各種不需要的文件描述符。
2.1.5 用fork來共享管道的原理
使用fork后
2.1.6 站在文件描述符角度-深度理解管道
0 1 2
分別為 標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤
。
2.1.7 站在內(nèi)核角度-管道的本質(zhì)
Linux下一切皆文件.
所以我們也應(yīng)該用看待文件的眼觀,去理解管道。
我們可以將管道(Pipe)理解為一種特殊類型的文件。實際上,管道確實是由操作系統(tǒng)內(nèi)部的內(nèi)存緩沖區(qū)實現(xiàn)的,它通過文件描述符來進(jìn)行訪問,就像其他普通文件一樣。通過這種類比,我們可以從文件的角度理解管道。
3. 匿名管道總結(jié)
通過匿名管道,進(jìn)程可以輕松地進(jìn)行數(shù)據(jù)交換,而不需要借助臨時文件或其他外部資源。盡管管道有一些局限性(如單向傳輸和緩沖區(qū)限制),它仍然是許多進(jìn)程間通信場景中常見的選擇。
注意:管道是半雙工的,數(shù)據(jù)只能向一個方向流動,需要雙方通信時,可以建立兩個管道。
到此這篇關(guān)于Linux管道揭秘:匿名管道如何連接進(jìn)程世界的文章就介紹到這了,更多相關(guān)Linux匿名管道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)字符格式相互轉(zhuǎn)換的示例代碼
這篇文章主要為大家詳細(xì)介紹了C++中實現(xiàn)字符格式相互轉(zhuǎn)換的方法,主要有UTF8與string互轉(zhuǎn)、wstring與string互轉(zhuǎn),感興趣的小伙伴可以了解一下2022-11-11C語言編程簡單卻重要的數(shù)據(jù)結(jié)構(gòu)順序表全面講解
這篇文章主要為大家介紹了C語言編程中非常簡單卻又非常重要的數(shù)據(jù)結(jié)構(gòu)順序表的全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10C++中rapidjson將map轉(zhuǎn)為json的方法
今天小編就為大家分享一篇關(guān)于C++中rapidjson將map轉(zhuǎn)為json的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04C++面試題之結(jié)構(gòu)體內(nèi)存對齊計算問題總結(jié)大全
這篇文章主要給大家總結(jié)了關(guān)于C++面試題中結(jié)構(gòu)體內(nèi)存對齊計算問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),通過這些介紹的內(nèi)容對大家在面試C++工作的時候,會有一定的參考幫助,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08