Linux:進程間通信-管道

Linux:進程間通信-管道

前言:為什么需要進程間通信?

你有沒有想過,當你在電腦上同時打開瀏覽器、音樂播放器和文檔時,這些程序是如何協同工作的?比如,瀏覽器下載的文件,為什么能被文檔編輯器直接打開?音樂播放器的音量調節,為什么能影響系統全局的聲音輸出?這背后,其實都是進程間通信(IPC)在發揮作用。

進程作為操作系統中獨立運行的基本單位,彼此之間默認是隔離的——就像住在不同房間的人,沒有門也沒有窗,無法直接交流。但實際應用中,進程又必須協同工作:比如打印進程需要接收文檔進程的數據,視頻渲染進程需要獲取解碼進程的結果。這就要求我們打破這種隔離,建立進程間的"溝通渠道"。

今天這篇文章,我們就從最基礎的管道開始,一步步揭開Linux進程間通信的神秘面紗。你會發現,看似復雜的IPC機制,其實和現實生活中的通信場景有著驚人的相似之處。

一、進程間通信的基本概念

1.1 什么是進程間通信?

進程間通信(IPC,Inter-Process Communication) 指的是兩個或多個進程之間進行數據交換的過程。它的本質是讓彼此獨立的進程能夠共享數據,實現協同工作。

舉個生活中的例子:你在廚房做飯(進程A),需要客廳的家人幫忙遞一下鹽(進程B)。這里的"遞鹽"就是一次簡單的IPC——你和家人(兩個進程)通過語言(通信方式)交換了"需要鹽"這個數據。

在計算機中,進程間的"語言"有很多種,比如管道、消息隊列、共享內存等,我們今天重點討論最基礎也最常用的"管道"。

1.2 為什么需要進程間通信?

你可能會說:"進程各自干好自己的事就行了,為什么非要通信?"但實際場景中,進程間的協同是必不可少的,主要體現在這幾個方面:

  • 數據傳輸:一個進程需要將數據發送給另一個進程。比如,輸入法進程需要把你輸入的文字發送給編輯器進程。
  • 資源共享:多個進程需要共享同一份資源(比如文件、內存)。比如,多個瀏覽器標簽頁需要共享同一個緩存文件。
  • 進程控制:一個進程需要控制另一個進程的行為。比如,任務管理器進程可以強制關閉無響應的程序進程。
  • 事件通知:一個進程需要向其他進程通知某個事件的發生。比如,下載進程完成后,通知用戶進程彈出提示。

想想看,如果沒有IPC,你的電腦會變成什么樣?瀏覽器下載的文件無法保存到硬盤(需要與文件系統進程通信),播放音樂時無法調節音量(需要與音頻進程通信),甚至連復制粘貼功能都無法實現(需要剪貼板進程在多個程序間傳遞數據)。

1.3 進程間通信的成本為什么高?

既然IPC這么重要,為什么實現起來不簡單呢?這就要從進程的"獨立性"說起了。

進程的獨立性是操作系統設計的基本原則——每個進程都有自己獨立的內存空間、寄存器狀態和文件描述符表。這種隔離性保證了一個進程的崩潰不會影響其他進程,但也給通信帶來了麻煩:進程A的內存數據,進程B默認是看不到的

就像兩個加密的保險箱,各自有獨立的密碼,不借助外部工具(比如鑰匙),里面的東西無法互通。要實現通信,就必須打破這種獨立性,建立共享資源——而創建和管理共享資源,必然會帶來系統開銷(比如內存分配、權限檢查)和復雜性(比如同步問題)。

舉個例子:如果進程A想給進程B發送數據,需要先把數據從A的用戶空間拷貝到內核空間的共享緩沖區,再由B從內核空間拷貝到自己的用戶空間(兩次拷貝)。這個過程比進程內部的數據訪問要慢得多,這就是通信的成本。

二、進程間通信的實現基礎

2.1 操作系統在IPC中扮演什么角色?

進程間通信不能靠進程自己"私下聯系",必須由操作系統作為"第三方協調者"。操作系統的作用主要有三個:

  1. 提供共享資源:比如創建管道、消息隊列等內核級資源,讓進程可以通過這些資源交換數據。
  2. 管理資源生命周期:負責創建、使用和釋放通信資源,避免資源泄露。
  3. 保證安全性和可控性:通過系統調用接口限制進程對資源的訪問,防止越權操作。

打個比方,操作系統就像一個中介:進程A和進程B想通信,先向中介申請一個"會議室"(共享資源),中介創建并管理這個會議室,A和B只能通過中介規定的方式進入會議室交流。

2.2 通信資源是如何管理的?

操作系統管理通信資源的核心原則是"先描述,再組織"。

  • 描述:每個通信資源(比如管道)都會被內核用一個數據結構(如struct pipe_inode_info)描述,記錄資源的屬性(大小、權限)、狀態(是否被使用)和操作方法(讀、寫函數)。
  • 組織:內核會把所有同類資源用鏈表或哈希表組織起來,方便查詢和管理。比如,所有管道會被放在一個全局鏈表中,操作系統可以通過遍歷鏈表找到某個特定管道。

這種管理方式就像圖書館的圖書管理:每本書(資源)都有一張卡片(描述結構),記錄書名、作者等信息;所有卡片按分類(組織方式)存放在卡片柜里,方便查找。

2.3 常見的IPC標準有哪些?

早期的Unix系統中,不同廠商實現的IPC機制各不相同,導致程序兼容性很差。后來行業逐漸形成了兩套主流標準:

  • System V IPC:由AT&T貝爾實驗室提出,主要包括消息隊列、信號量和共享內存三種方式,適用于單機內的進程通信。
  • POSIX IPC:由IEEE制定,兼容System V的部分功能,同時支持線程通信和網絡通信,接口更統一,現在應用更廣泛。

這兩套標準就像通信領域的"普通話",讓不同進程(甚至不同程序語言編寫的進程)能按照統一的規則交流。

三、管道:最古老的IPC方式

3.1 什么是管道?

管道(Pipe)是Unix系統中最古老的IPC方式,它的設計非常樸素:用內存中的文件緩沖區模擬"管道",讓一個進程往管道里寫數據,另一個進程從管道里讀數據

你可以把管道想象成一根水管:一端進水(寫端),另一端出水(讀端),水(數據)在管內單向流動。這種單向性是管道的核心特征——就像現實中的水管,你不能同時從一端既進水又出水。

在Linux命令行中,你其實早就用過管道了。比如ps aux | grep "chrome"這個命令,ps進程的輸出通過|(管道符號)傳遞給grep進程,這里的|就是一個匿名管道。

3.2 管道的實現原理

管道本質上是一個內存級文件,它有這些特點:

  • 不在磁盤上存儲,數據只存在于內存緩沖區中。
  • 遵循文件操作的接口(打開、讀、寫、關閉),但不需要刷新到磁盤。
  • 通過文件描述符表讓進程訪問:一個描述符對應讀端,另一個對應寫端。

具體實現步驟如下:

  1. 創建管道:通過pipe()系統調用創建管道,內核會分配一個內存緩沖區,并返回兩個文件描述符:fd[0](讀端)和fd[1](寫端)。
  2. 創建子進程:通過fork()創建子進程,子進程會繼承父進程的文件描述符表,因此也能訪問同一個管道。
  3. 關閉無用端口:父進程關閉讀端(fd[0]),子進程關閉寫端(fd[1]),形成單向通信信道(父寫子讀);或者反過來(父讀子寫)。
  4. 通信:父進程通過write()fd[1]寫數據,子進程通過read()fd[0]讀數據。

舉個例子:父進程想給子進程發送"hello",步驟如下:

  • 父進程調用pipe(fd),得到fd[0]=3(讀)、fd[1]=4(寫)。
  • 父進程fork()出子進程,子進程的fd數組也是[3,4]
  • 父進程close(fd[0])(關閉讀端),子進程close(fd[1])(關閉寫端)。
  • 父進程write(fd[1], "hello", 5),子進程read(fd[0], buf, 5),最終buf中就有"hello"。

3.3 管道的代碼實現

下面我們用C語言實現一個簡單的父子進程管道通信:父進程向子進程發送消息,子進程打印消息。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>int main() {int fd[2];// 1. 創建管道if (pipe(fd) == -1) {perror("pipe error");exit(1);}// 2. 創建子進程pid_t pid = fork();if (pid == -1) {perror("fork error");exit(1);}if (pid == 0) { // 子進程:讀數據close(fd[1]); // 關閉寫端char buf[1024];ssize_t len = read(fd[0], buf, sizeof(buf)-1);if (len > 0) {buf[len] = '\0';printf("子進程收到:%s\n", buf);}close(fd[0]); // 關閉讀端exit(0);} else { // 父進程:寫數據close(fd[0]); // 關閉讀端const char* msg = "你好,子進程!";write(fd[1], msg, strlen(msg));close(fd[1]); // 關閉寫端(觸發子進程讀結束)wait(NULL); // 等待子進程退出exit(0);}
}

編譯運行后,會輸出:子進程收到:你好,子進程!

這里有幾個關鍵點:

  • 子進程必須關閉寫端,父進程必須關閉讀端,否則會導致阻塞(比如子進程讀完數據后,會一直等父進程寫更多數據)。
  • 當寫端關閉后,讀端read()會返回0,表示數據已讀完(類似文件結束)。
  • 管道的緩沖區大小是固定的(通常為64KB),如果寫端寫滿緩沖區,會阻塞直到讀端讀取數據釋放空間。

3.4 管道的五大特性

通過上面的例子,我們可以總結出管道的五個核心特性:

  1. 只能單向通信:管道是半雙工的,數據只能從一端到另一端。如果需要雙向通信,必須創建兩個管道。

    (思考:為什么管道設計成單向的?其實是為了簡化實現——雙向通信需要更復雜的同步機制,而單向通信能滿足大部分場景。)

  2. 只能用于有血緣關系的進程:因為管道沒有名字,只能通過fork()繼承文件描述符的方式讓進程共享。父子進程、兄弟進程(同一個父進程創建)之間可以用管道通信,但兩個無關進程不行。

  3. 面向字節流:管道中的數據是連續的字節流,沒有消息邊界。比如,父進程分兩次寫"hello"和"world",子進程可能一次就讀到"helloworld"。

    (注意:這意味著應用程序需要自己定義協議來區分消息,比如用換行符分隔,或固定消息長度。)

  4. 自帶同步機制

    • 讀端:如果管道為空,read()會阻塞,直到有數據寫入。
    • 寫端:如果管道滿了,write()會阻塞,直到有數據被讀走。
  5. 生命周期隨進程:管道會在所有訪問它的進程都關閉文件描述符后,被內核自動銷毀。

3.5 管道的四種典型情況

管道通信中,讀寫端的狀態會直接影響通信行為,常見的四種情況需要特別注意:

情況現象原因
讀寫端正常,管道為空讀端阻塞讀端等待寫端寫入數據
讀寫端正常,管道滿了寫端阻塞寫端等待讀端讀取數據釋放空間
讀端關閉,寫端繼續寫寫端進程被殺死操作系統發送SIGPIPE信號終止寫進程(避免無效寫入)
寫端關閉,讀端繼續讀讀端讀到0(文件結束)寫端關閉后,管道中剩余數據讀完后,read()返回0

比如,如果你在代碼中忘了關閉寫端,子進程的read()會一直阻塞(以為還有數據要讀),導致程序卡死。這也是為什么我們強調"一定要關閉無用的文件描述符"。

四、命名管道:讓無關進程也能通信

4.1 匿名管道的局限性

匿名管道雖然簡單,但有個致命缺點:只能用于有血緣關系的進程。如果兩個完全無關的進程(比如瀏覽器和音樂播放器)想通信,匿名管道就無能為力了——因為它們無法共享文件描述符。

這就像兩個陌生人住在不同的小區,沒有共同的朋友(父進程)介紹,無法知道對方的地址(管道的文件描述符)。要解決這個問題,就需要一種"有名字"的管道——命名管道(FIFO)。

4.2 什么是命名管道?

命名管道(FIFO,First In First Out)和匿名管道的核心原理相同,但它有一個關鍵區別:命名管道有文件名和路徑,可以通過文件系統被所有進程訪問。

就像一個公共郵箱:任何知道郵箱地址(路徑)的人,都可以往里面放信(寫數據)或取信(讀數據),不需要彼此認識。

在Linux中,你可以用mkfifo命令創建命名管道:

mkfifo myfifo  # 創建一個名為myfifo的命名管道

創建后,你會在目錄中看到這個文件,類型為p(管道):

ls -l myfifo
# 輸出:prw-r--r-- 1 user user 0 8月  21 10:00 myfifo

4.3 命名管道的使用方式

命名管道的使用步驟和文件操作類似,分為創建、打開、讀寫、關閉四個步驟:

  1. 創建:用mkfifo命令或mkfifo()函數創建。

    #include <sys/stat.h>
    int mkfifo(const char *pathname, mode_t mode);
    // 參數:pathname(管道路徑)、mode(權限,如0666)
    // 返回值:0成功,-1失敗
    
  2. 打開:用open()函數打開,指定讀或寫模式。

    int fd = open("myfifo", O_RDONLY);  // 只讀打開(讀端)
    // 或
    int fd = open("myfifo", O_WRONLY);  // 只寫打開(寫端)
    
  3. 讀寫:用read()write()函數操作,和匿名管道相同。

  4. 關閉:用close()關閉文件描述符。

  5. 刪除:用unlink()函數刪除管道文件(類似rm命令)。

4.4 命名管道的代碼實現

下面我們實現兩個無關進程的通信:一個寫進程向命名管道發送消息,一個讀進程接收消息。

寫進程(writer.c)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>int main() {// 1. 創建命名管道(如果不存在)if (mkfifo("myfifo", 0666) == -1) {perror("mkfifo error");exit(1);}// 2. 打開管道(寫端)int fd = open("myfifo", O_WRONLY);if (fd == -1) {perror("open error");exit(1);}// 3. 發送消息const char* msg = "來自writer的消息:你好,reader!";write(fd, msg, strlen(msg));printf("發送成功\n");// 4. 關閉管道close(fd);// 5. 刪除管道(可選)unlink("myfifo");return 0;
}

讀進程(reader.c)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>int main() {// 1. 打開管道(讀端)int fd = open("myfifo", O_RDONLY);if (fd == -1) {perror("open error");exit(1);}// 2. 接收消息char buf[1024];ssize_t len = read(fd, buf, sizeof(buf)-1);if (len > 0) {buf[len] = '\0';printf("收到消息:%s\n", buf);}// 3. 關閉管道close(fd);return 0;
}

運行步驟:

  1. 編譯兩個程序:gcc writer.c -o writergcc reader.c -o reader
  2. 先啟動讀進程:./reader(會阻塞等待寫端打開)。
  3. 再啟動寫進程:./writer(發送消息后退出)。
  4. 讀進程會輸出:收到消息:來自writer的消息:你好,reader!

4.5 命名管道與匿名管道的區別

特性匿名管道命名管道
存在形式內存中,無文件名有文件名(在文件系統中可見)
適用進程有血緣關系(父子、兄弟)任意進程(只要知道路徑)
創建方式pipe()系統調用mkfifo()函數或mkfifo命令
打開方式繼承文件描述符通過open()函數打開路徑
生命周期隨進程(所有進程關閉后銷毀)隨文件(需用unlink()刪除)

本質上,命名管道只是比匿名管道多了一個"文件名",其他特性(單向通信、面向字節流、同步機制)完全相同。

五、基于管道的進程池設計

5.1 什么是進程池?

在實際開發中,我們經常需要創建多個子進程處理任務(比如服務器處理多個客戶端請求)。如果每次有任務才創建子進程,會帶來很大的開銷(創建進程需要分配內存、初始化PCB等)。

進程池就是一種優化方案:提前創建一批子進程,當有任務時,直接讓空閑的子進程處理,避免頻繁創建和銷毀進程

就像餐廳的服務員團隊:開業前招聘好服務員(創建子進程),客人來了(任務)直接安排空閑服務員接待,不用等客人來了再臨時招聘。

5.2 基于管道的進程池通信模型

進程池的核心是父進程如何給子進程分配任務。我們可以用管道實現這種通信:

  1. 創建進程池:父進程創建N個子進程,為每個子進程創建一個管道(父寫子讀)。
  2. 子進程等待任務:每個子進程阻塞在管道的讀端,等待父進程發送任務。
  3. 父進程分配任務:父進程有任務時,選擇一個空閑子進程,通過對應的管道發送任務數據。
  4. 子進程處理任務:子進程收到任務后,執行任務,完成后繼續等待下一個任務。

這種模型的優點是:

  • 父進程可以精確控制每個子進程的任務(通過不同管道)。
  • 子進程專注于處理任務,不需要關心任務分配邏輯。

5.3 進程池代碼實現

下面我們實現一個簡單的進程池:父進程創建3個子進程,向它們發送不同的任務(打印不同的消息)。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <vector>
#include <string>
#include <cstring>// 任務結構體(這里簡化為字符串)
struct Task {std::string msg;
};// 子進程處理任務的函數
void handle_task(int read_fd) {while (true) {// 讀取任務char buf[1024];ssize_t len = read(read_fd, buf, sizeof(buf)-1);if (len <= 0) break; // 寫端關閉,退出buf[len] = '\0';printf("子進程[%d]處理任務:%s\n", getpid(), buf);}close(read_fd);exit(0);
}int main() {const int NUM_PROCESSES = 3; // 進程池大小std::vector<int> write_fds;  // 保存每個子進程對應的寫端// 創建進程池for (int i = 0; i < NUM_PROCESSES; ++i) {int fd[2];if (pipe(fd) == -1) {perror("pipe error");exit(1);}pid_t pid = fork();if (pid == -1) {perror("fork error");exit(1);}if (pid == 0) { // 子進程close(fd[1]); // 關閉寫端handle_task(fd[0]);} else { // 父進程close(fd[0]); // 關閉讀端write_fds.push_back(fd[1]); // 保存寫端}}// 向子進程發送任務std::vector<Task> tasks = {{"任務1:打印日志"},{"任務2:處理數據"},{"任務3:發送網絡請求"},{"任務4:更新緩存"},{"任務5:生成報表"}};for (size_t i = 0; i < tasks.size(); ++i) {int fd = write_fds[i % NUM_PROCESSES]; // 輪詢分配任務write(fd, tasks[i].msg.c_str(), tasks[i].msg.size());sleep(1); // 間隔1秒發送}// 關閉所有寫端(觸發子進程退出)for (int fd : write_fds) {close(fd);}// 等待所有子進程退出for (int i = 0; i < NUM_PROCESSES; ++i) {wait(NULL);}return 0;
}

運行后,輸出類似:

子進程[1234]處理任務:任務1:打印日志
子進程[1235]處理任務:任務2:處理數據
子進程[1236]處理任務:任務3:發送網絡請求
子進程[1234]處理任務:任務4:更新緩存
子進程[1235]處理任務:任務5:生成報表

這個例子中,父進程通過輪詢的方式給3個子進程分配5個任務,子進程處理完后繼續等待新任務,直到父進程關閉寫端才退出。

5.4 進程池的優化方向

上面的簡單實現可以進一步優化:

  • 動態擴容:當任務過多時,自動創建新的子進程;任務過少時,銷毀部分子進程。
  • 任務優先級:給任務設置優先級,父進程按優先級分配。
  • 結果返回:子進程處理完任務后,通過另一個管道將結果返回給父進程。
  • 異常處理:子進程崩潰時,父進程能檢測到并重新創建子進程。

這些優化可以讓進程池更適應實際應用場景,比如高并發的服務器程序。

六、其他IPC方式簡介

除了管道,Linux還有其他常用的IPC方式,這里簡單介紹:

6.1 消息隊列

消息隊列是內核中的一個消息鏈表,進程可以向隊列中添加消息,也可以從隊列中讀取消息。每個消息都有類型,讀取時可以按類型篩選。

優點:可以實現雙向通信,消息有邊界(不需要自己定義協議)。
缺點:消息大小和隊列長度有限制,效率不如共享內存。

6.2 信號量

信號量不是用于傳遞數據,而是用于實現進程間的同步和互斥(比如控制多個進程對共享資源的訪問)。

比如,信號量可以比作停車場的車位計數器:進程要進入臨界區(停車場),需要先獲取信號量(車位);離開時釋放信號量(騰出車位)。

6.3 共享內存

共享內存是效率最高的IPC方式:操作系統在內存中開辟一塊區域,讓多個進程直接映射到自己的地址空間,進程可以直接讀寫這塊內存,不需要內核中轉。

優點:數據不需要拷貝,速度極快。
缺點:需要自己處理同步問題(比如用信號量防止同時寫入)。

七、總結與展望

進程間通信是操作系統中非常重要的概念,而管道作為最基礎的IPC方式,雖然簡單但應用廣泛。通過本文的學習,你應該掌握:

  • 進程間通信的必要性和成本來源。
  • 匿名管道的原理、實現和特性(單向通信、血緣關系限制)。
  • 命名管道如何解決匿名管道的局限性,讓無關進程通信。
  • 基于管道的進程池設計,理解如何高效管理多個子進程。

管道雖然好用,但在高并發、大數據量的場景下,可能需要更高效的方式(如共享內存)。下一篇文章,我們將深入探討共享內存的實現原理和使用技巧,敬請期待!

7.1、為什么管道的緩沖區大小是固定的?動態調整緩沖區大小有什么問題?

管道的緩沖區大小被設計為固定值(通常為64KB,不同內核版本可能略有差異),核心原因是簡化操作系統對管道的管理,并保證通信的穩定性和效率。具體來說:

  1. 固定大小便于內核管理
    管道的緩沖區是內核維護的一塊連續內存。固定大小可以讓內核提前分配內存、設置邊界,避免頻繁的動態內存申請/釋放(比如用kmallocvmalloc)。動態調整需要內核實時計算所需空間、處理內存碎片,會增加系統開銷,降低通信效率。

  2. 避免進程通信的不確定性
    如果緩沖區大小動態變化,進程無法預判寫入/讀取的邊界。比如,寫進程可能以為緩沖區足夠大而持續寫入,導致內存耗盡;讀進程也無法確定何時能讀完數據,容易引發阻塞或數據截斷。固定大小能讓進程明確通信的“上限”,便于設計可靠的讀寫邏輯。

動態調整緩沖區大小的主要問題:

  • 同步復雜:緩沖區擴容/縮容時,正在進行的讀寫操作可能被打斷,需要內核額外加鎖保護,增加死鎖風險。
  • 效率下降:動態內存分配(尤其是大內存)耗時較長,且可能因內存碎片導致分配失敗,影響管道的實時性。
  • 接口不統一:用戶進程無法提前知曉緩沖區大小,難以設計兼容不同內核版本的代碼(不同系統動態調整策略可能不同)。

7.2、如何用兩個管道實現父子進程的雙向通信?

管道是單向通信的(“半雙工”),但通過創建兩個管道,可以讓父子進程實現雙向通信。核心思路是:

  • 管道1:父進程寫,子進程讀(父→子方向)。
  • 管道2:子進程寫,父進程讀(子→父方向)。

具體步驟(代碼示例):

  1. 創建兩個管道
    pipe()創建兩個管道pipe1pipe2,分別對應兩個方向的通信信道。

    int pipe1[2], pipe2[2];
    pipe(pipe1);  // pipe1[0]:讀端;pipe1[1]:寫端(父→子)
    pipe(pipe2);  // pipe2[0]:讀端;pipe2[1]:寫端(子→父)
    
  2. 創建子進程并關閉無關端口
    父子進程通過fork()繼承管道的文件描述符后,需關閉不需要的端口,避免干擾:

    • 父進程:關閉pipe1的讀端(pipe1[0])和pipe2的寫端(pipe2[1]),保留pipe1[1](寫)和pipe2[0](讀)。
    • 子進程:關閉pipe1的寫端(pipe1[1])和pipe2的讀端(pipe2[0]),保留pipe1[0](讀)和pipe2[1](寫)。
  3. 雙向通信

    • 父進程通過write(pipe1[1], ...)向子進程發送數據,子進程通過read(pipe1[0], ...)接收。
    • 子進程通過write(pipe2[1], ...)向父進程回復數據,父進程通過read(pipe2[0], ...)接收。

代碼片段示例:

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main() {int pipe1[2], pipe2[2];pipe(pipe1);  // 父→子pipe(pipe2);  // 子→父pid_t pid = fork();if (pid == 0) {  // 子進程close(pipe1[1]);  // 關閉pipe1寫端close(pipe2[0]);  // 關閉pipe2讀端// 接收父進程數據char buf[100];read(pipe1[0], buf, sizeof(buf));printf("子進程收到:%s\n", buf);// 向父進程回復const char* reply = "子進程已收到!";write(pipe2[1], reply, strlen(reply));close(pipe1[0]);close(pipe2[1]);} else {  // 父進程close(pipe1[0]);  // 關閉pipe1讀端close(pipe2[1]);  // 關閉pipe2寫端// 向子進程發送數據const char* msg = "父進程:你好!";write(pipe1[1], msg, strlen(msg));// 接收子進程回復char buf[100];read(pipe2[0], buf, sizeof(buf));printf("父進程收到:%s\n", buf);close(pipe1[1]);close(pipe2[0]);}return 0;
}

運行后輸出:

子進程收到:父進程:你好!
父進程收到:子進程已收到!

7.3、命名管道在文件系統中可見,但數據不寫入磁盤,這是如何實現的?

命名管道(FIFO)在文件系統中可見(有路徑和文件名),但數據不寫入磁盤,核心原因是它本質是“內存級文件”,文件系統中的條目僅作為“標識”,不存儲實際數據。具體實現如下:

  1. 文件系統中的“標識”作用
    命名管道通過mkfifo創建時,內核會在文件系統中創建一個特殊的inode(索引節點),記錄管道的路徑、權限、創建者等元信息,但不分配磁盤數據塊。這個inode的作用是讓所有進程通過路徑找到同一個管道(類似“地址牌”),而非存儲數據。

  2. 數據存儲在內存緩沖區
    命名管道的實際數據存儲在內核維護的內存緩沖區中(和匿名管道一樣)。當進程通過open打開命名管道時,內核會將管道的內存緩沖區映射到進程的文件描述符表中,進程的read/write操作實際是讀寫這塊內存,而非磁盤。

  3. 不寫入磁盤的原因
    命名管道設計的核心是“進程間臨時通信”,數據無需持久化。如果寫入磁盤,會帶來額外的I/O開銷(磁盤速度遠慢于內存),且通信結束后數據無用,反而浪費磁盤空間。內核通過將數據限制在內存中,既保證了通信效率,又避免了不必要的磁盤操作。

簡單說:命名管道在文件系統中的“可見性”只是為了讓進程找到它,而實際數據始終在內存中流轉,用完即棄,不會落地到磁盤。
歡迎在評論區留下你的答案和疑問,我們一起討論!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94137.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94137.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94137.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Jmeter + FFmpeg 直播壓測遇到的問題及解決方案

1、壓測機安裝FFmpeg&#xff0c;下載安裝步驟可見&#xff1a;https://zhuanlan.zhihu.com/p/692019886 2、Jmeter與FFmpeg位數要一致&#xff0c;不允許在32位的進程中運行一個64位的程序&#xff0c;反之亦然 3、OS進程取樣器&#xff08;Thread Group -> Add -> Sa…

安卓app、微信小程序等訪問多個api時等待提示調用與關閉問題

安卓app、微信小程序訪問webapi&#xff0c;將需要一時間&#xff0c;我們稱之為耗時操作&#xff0c;其它諸如密集型計算、訪問文件與設備等亦是如此。在這個期間我們應該跳出提示&#xff0c;告知用戶正在等待&#xff0c;并且很多時候&#xff0c;在等待時不允許用戶再對UI進…

一個狀態機如何啟動/停止另一個狀態機

一個狀態機如何啟動/停止另一個狀態機 這個過程主要依賴于動作列表&#xff08;Action List&#xff09; 中的特定動作項和狀態管理服務&#xff08;ARA::SM&#xff09;提供的API。 1. 通過動作列表&#xff08;Action List&#xff09;進行預配置控制 這是最常見的方式&#…

基于IPO智能粒子優化的IIR濾波器參數識別算法matlab仿真

目錄 1.程序功能描述 2.測試軟件版本以及運行結果展示 3.部分程序 4.算法理論概述 5.完整程序 1.程序功能描述 IIR&#xff08;Infinite Impulse Response&#xff09;濾波器即無限沖激響應濾波器&#xff0c;其輸出不僅與當前和過去的輸入有關&#xff0c;還與過去的輸出…

歐州服務器String 轉 double 有BUG?

string 轉 double 的常見問題通常與文化差異、格式解析或特殊值處理相關&#xff0c;而非框架本身的 “BUG”。以下是可能導致轉換異常的常見場景及解決方案&#xff1a; 文化差異導致的解析問題 現象&#xff1a;同樣的字符串&#xff08;如 “1.23” 或 “1,23”&#xff09;…

鴻蒙中網絡診斷:Network分析

上面的圖很熟悉吧 Network 面板的表格列出了所有請求&#xff0c;每一列都提供了關鍵信息&#xff1a; Name: 請求的資源名稱和路徑。 Status: HTTP 狀態碼&#xff08;診斷核心&#xff09;。200成功&#xff0c;304未修改&#xff08;緩存&#xff09;&#xff0c;404找不到…

HarmonyOS 實戰:6 種實現實時數據更新的方案全解析(含完整 Demo)

摘要 在當下的應用開發中&#xff0c;用戶體驗越來越依賴“實時性”。消息要第一時間送達、訂單狀態要立刻刷新、數據變化不能延遲……這些需求推動了“實時數據更新”成為應用的必備功能。在鴻蒙系統&#xff08;HarmonyOS&#xff09;中&#xff0c;我們既可以用系統內置的數…

第十六屆藍橋杯青少組C++省賽[2025.8.10]第二部分編程題(4、矩陣圈層交錯旋轉)

參考程序&#xff1a;#include <bits/stdc.h> using namespace std;const int MAXN 105; int a[MAXN][MAXN];int main() {int n;if (!(cin >> n)) return 0;for (int i 0; i < n; i)for (int j 0; j < n; j)cin >> a[i][j];int layers n / 2; // 每…

AI供應鏈情報預警 | 惡意Py包偽裝AI框架庫開展數據竊密及應用劫持攻擊

AI供應鏈情報概述近日&#xff08;18th Aug. , 2025&#xff09;&#xff0c;懸鏡安全情報中心在Python官方倉庫中捕獲1起偽裝成知名AI框架庫pytensor&#xff08;https://pypi.org/project/pytensor&#xff09;的組件投毒事件。在北京時間8月18日凌晨&#xff0c;投毒者連續發…

AI需要防火墻,云計算需要重新構想

Akamai創始人Tom Leighton欲終結云膨脹&#xff0c;從內到外守護AI安全 Akamai創始人Tom Leighton 當前超大規模云服務商主導著企業IT市場&#xff0c;鮮有人敢挑戰云計算經濟模式、AI基礎設施和網絡安全架構的現狀。但Akamai聯合創始人兼CEO Tom Leighton正是這樣的挑戰者。他…

線段樹詳解【數據結構】

簡介 線段樹是一種應用極其廣泛&#xff0c;使用范圍較廣并且非常知名的樹形數據結構&#xff0c;主要用于進行區間操作&#xff0c;如區間修改&#xff0c;區間查詢等。這種數據結構唯一的不足就是巨大的代碼量&#xff0c;因此處理一些較簡單的問題時建議用樹狀數組。 原理…

Maven 入門與進階:聚合、繼承與生命周期詳解

Maven 是 Java 項目管理的核心工具&#xff0c;其強大的依賴管理、項目構建和模塊化設計能力&#xff0c;極大地提升了開發效率。本文將深入探討 Maven 的 聚合&#xff08;Multi-module&#xff09;、繼承&#xff08;Inheritance&#xff09; 和 生命周期&#xff08;Lifecyc…

手搓MCP客戶端動態調用多MCP服務,調用哪個你說了算!

01 引言 前兩天&#xff0c;有個粉絲朋友咨詢MCP服務如何動態調用&#xff0c;動態加載MCP服務的鏈接&#xff1f;我們都知道MCP客戶端可以配置多個MCP服務的地址&#xff1a; spring.ai.mcp.client.sse.connections.server1.urlhttp://localhost:xxxx spring.ai.mcp.client.ss…

Go語言中的優雅并發控制:通道信號量模式詳解

在Go語言的并發編程中&#xff0c;“通過通信共享內存”的設計哲學貫穿始終。當面對高并發場景時&#xff0c;無限制創建goroutine可能導致資源耗盡、CPU過載等問題&#xff0c;通道信號量模式&#xff08;Channel Semaphore Pattern&#xff09; 正是一種基于Go通道特性的優雅…

鴻蒙 NEXT開發中輕松實現人臉識別功能

大家好&#xff0c;我是 V 哥。 今天給大家介紹在 HarmonyOS 原生鴻蒙開發中&#xff0c;實現人臉識別功能&#xff0c;這個功能在常用的 APP 開發中上鏡率還是很高的&#xff0c;在傳統的 Android 或 iOS 開發中&#xff0c;通常我們要借助第三方庫來實現&#xff0c;而在鴻蒙…

華為開發者空間訓練營-優秀作品公布

排名標題總分獎品1手把手教你開發一個地區智能查詢MCP&#xff0c;賦能地理位置類MCP服務的“零輸入”無感交互95華為 freebuds 6i 藍牙耳機2基于華為開發者空間云主機DeepSeek助力電商企業AI海報文案驅動的最佳實踐落地 94華為 freebuds 6i 藍牙耳機32小時基于華為開發者空間和…

基于Python與Tkinter開發的微博多功能自動化助手

文章目錄 摘要 1. 背景與意義 2. 需求分析 3. 核心架構設計 3.1. 技術選型 3.2. 核心思想:UI與邏輯分離的異步架構 4. 深度模塊化剖析 4.1. 微博核心API交互模塊 4.2. 健壯性設計:代理與重試機制 4.3. GUI界面模塊 (WeiboApp 類) 4.4. 異步任務處理模塊 5. 難點分析與解決方案…

效果驅動復購!健永科技RFID牛場智能稱重項目落地

近日&#xff0c;北京某養殖企業持續下單電子耳標識讀器&#xff0c;在牛場智能稱重中落地應用&#xff0c;通過自動、準確地識別牛只并記錄體重數據&#xff0c;顯著提升效率和數據精準度&#xff0c;實現了“效果驅動復購”的良性循環。健永科技RFID技術在北京某養殖企業智能…

計算機網絡:2、TCP和UDP

2、TCP和UDP 簡介 TCP(transmission Control Protocol)&#xff1a;是一種通信標準&#xff0c;它使應用程序和計算設備能夠在網絡上交換消息。它的設計目的是在互聯網上發送數據包&#xff0c;并確保數據和信息在網絡上的成功傳遞。UDP(the User Datagram Protocol)&#xf…

WEB安全篇:瀏覽器攻擊原理及防護

1、XSS&#xff1a;跨站腳本攻擊就是攻擊者想盡一切辦法將可以執行的代碼注入到網頁中。攻擊者在web頁面惡意插入HTML或script標簽&#xff0c;當用戶瀏覽該頁面時&#xff0c;惡意代碼就會被執行&#xff0c;從而達到攻擊的目的。XSS利用的是用戶對指定網站的信任。比如&#…