首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言進程間通訊:管道實作詳解 (pipe/fork/read/write)

     編輯:關於C語言

C 語言進程間通訊:管道實作詳解

管道是 Unix/Linux 系統中最經典的進程間通訊 (IPC) 機制之一,提供單向資料流,配合 fork() 實現父子進程協作。本文深入解析 pipe() 系統呼叫、檔案描述符機制、讀寫同步與完整程式碼實作。

pipe() 系統呼叫原型與語意

#include 
int pipe(int file_descriptor[2]);
  • 參數:整數陣列 fd[2],回傳兩個檔案描述符
  • fd[0]:讀取端 (對應標準輸入 stdin)
  • fd[1]:寫入端 (對應標準輸出 stdout)
  • 回傳值:成功返回 0,失敗返回 -1 並設定 errno
  • 資料流向:寫入端 → 管道緩衝區 → 讀取端

管道通訊標準流程

  1. 父進程呼叫 pipe() 建立管道,獲得 fd[0]、fd[1]
  2. 父進程呼叫 fork() 建立子進程,兩進程共享相同檔案描述符表
  3. 子進程:關閉讀取端 fd[0],透過 write(fd[1], ...) 發送資料
  4. 父進程:關閉寫入端 fd[1],透過 read(fd[0], ...) 接收資料
  5. 使用 wait() 同步,確保子進程寫入完成後父進程再讀取

完整程式碼實作

#include 
#include 
#include 
#include 
#include 

#define BUFSIZ 1024

int main(void) {
    char input[] = "IPC by pipe";
    char output[BUFSIZ + 1];
    int fd[2];
    ssize_t count = 0;
    int status;
    pid_t pid, pid_ret;

    memset(output, 0, sizeof(output));

    // 1. 建立管道
    if (pipe(fd) == -1) {
        perror("pipe 建立失敗");
        return 1;
    }

    // 2. 建立子進程
    pid = fork();
    if (pid > 0) {                    // 父進程
        close(fd[1]);                 // 關閉寫入端
        pid_ret = wait(&status);      // 等待子進程結束
        if (pid_ret > 0) {
            count = read(fd[0], output, BUFSIZ);
            printf("父進程讀取 %zd 字元: %s\n", count, output);
        }
        close(fd[0]);
    } else if (pid == 0) {            // 子進程
        close(fd[0]);                 // 關閉讀取端
        count = write(fd[1], input, strlen(input));
        printf("子進程寫入 %zd 字元: %s\n", count, input);
        close(fd[1]);
        exit(0);
    } else {
        perror("fork 失敗");
        return 1;
    }

    return 0;
}

執行結果

$ ./pipe_demo
子進程寫入 11 字元: IPC by pipe
父進程讀取 11 字元: IPC by pipe

關鍵技術點深度解析

檔案描述符繼承與關閉

fork() 會複製父進程的檔案描述符表,**父子進程共享同一管道物件**。必須在各自進程中**關閉不使用的端點**,否則:

  • 讀取端未關閉 → read() 永遠不會返回 EOF (因寫入端仍開啟)
  • 寫入端未關閉 → 管道無法檢測讀取端關閉,寫入端收不到 SIGPIPE

阻塞特性與同步

  • read():管道無資料時阻塞,直到有資料或寫入端關閉
  • write():管道緩衝區滿時阻塞 (預設 64KB),直到有空間
  • wait():父進程同步等待子進程退出,避免殭屍進程

管道容量與原子性

  • Linux 管道容量:cat /proc/sys/fs/pipe-max-size (通常 65536 位元組)
  • PIPE_BUF (POSIX 保證 ≥ 512 位元組) 內寫入為**原子操作**,多進程並發寫入不會交錯

進階變體:命名管道 (FIFO)

適用於**無親緣關係進程**間通訊:

// 建立命名管道
mkfifo("/tmp/my_fifo", 0666);

// 寫入端
int fd = open("/tmp/my_fifo", O_WRONLY);
write(fd, "data", 4);
close(fd);

// 讀取端
int fd = open("/tmp/my_fifo", O_RDONLY);
read(fd, buf, sizeof(buf));
close(fd);

常見錯誤與除錯技巧

錯誤現象原因修正方法
程式卡住不動未關閉對端描述符,read/write 永久阻塞各進程務必 close() 不用的 fd[0] 或 fd[1]
讀取到亂碼/不完整緩衝區未清零、長度計算錯誤memset 清零、使用 strlen/實際讀取長度
Broken pipe / SIGPIPE讀取端關閉後繼續寫入捕捉 SIGPIPE 或檢查 write 回傳值
殭屍進程父進程未 wait() 子進程加入 wait()/waitpid() 或 signal(SIGCHLD, SIG_IGN)

管道 vs 其他 IPC 機制選型指南

  • 匿名管道:父子進程、單向、簡單資料流
  • 命名管道 (FIFO):無親緣進程、單向、檔案系統可見
  • Socketpair:雙向、父子進程、高效
  • Unix Domain Socket:雙向、任意進程、支援傳遞檔案描述符
  • 共享記憶體:高效大量資料、需自行同步 (信號量/互斥鎖)
  • 訊息佇列:結構化訊息、持久化、系統級
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved