管道是進程間通信(IPC)的經典機制。本文聚焦命名管道(FIFO):它是文件系統中真實存在的特殊文件,支持無親緣關係進程通信。通過 mkfifo 創建、open/read/write/close 操作,演示父子進程雙向數據傳輸全過程。
ls -l 可見,類型 p,如 prw-r--r--。open 讀端阻塞直到寫端打開,反之亦然;read 無數據阻塞,write 緩衝區滿阻塞。#include#include // 創建命名管道 int mkfifo(const char *pathname, mode_t mode); // 成功返回 0,失敗 -1(errno=EEXIST 表示已存在) // 後續按普通文件操作 int fd = open(pathname, O_RDONLY | O_WRONLY | O_NONBLOCK); read(fd, buf, len); write(fd, buf, len); close(fd);
#include#include #include #include #include #include #include #define FIFO_PATH "/tmp/test_fifo" #define BUFSIZ 1024 int main() { char input[] = "IPC by FIFO"; char output[BUFSIZ+1] = {0}; int fd, count = 0; pid_t pid, pid_res; int stat_value; // 1. 創建命名管道 if (mkfifo(FIFO_PATH, 0777) == -1) { perror("mkfifo"); return 1; } // 2. 創建子進程 pid = fork(); if (pid < 0) { perror("fork"); return 1; } if (pid > 0) { // 父進程:讀取 printf("Father running (PID=%d)\n", getpid()); fd = open(FIFO_PATH, O_RDONLY); if (fd == -1) { perror("open read"); return 1; } pid_res = wait(&stat_value); // 等待子進程寫入結束 if (pid_res > 0) { count = read(fd, output, BUFSIZ); printf("Father read %d bytes: %s\n", count, output); close(fd); } } else { // 子進程:寫入 printf("Son running (PID=%d)\n", getpid()); fd = open(FIFO_PATH, O_WRONLY); if (fd == -1) { perror("open write"); return 1; } count = write(fd, input, strlen(input)); printf("Son wrote %d bytes: %s\n", count, input); close(fd); exit(0); } // 清理(可選,演示用) unlink(FIFO_PATH); return 0; }
Father running (PID=12345) Son running (PID=12346) Son wrote 12 bytes: IPC by FIFO Father read 12 bytes: IPC by FIFO
open(O_RDONLY) 會阻塞,直到子進程 open(O_WRONLY)。close(fd),發送 EOF。read 讀到 EOF 返回 0,或讀到數據返回字節數。wait 同步等待子進程退出,避免競態。| 類型 | 創建 | 通信雙方 | 生命週期 | 適用場景 |
|---|---|---|---|---|
匿名管道 pipe() | 內存緩衝區 | 親緣進程(父子/兄弟) | 隨進程結束銷毀 | 父子進程簡單數據流 |
popen() 偽管道 | Shell 命令管道 | 進程與 Shell 命令 | 隨 pclose 結束 | 執行命令捕獲輸出 |
命名管道 mkfifo() | 文件系統特殊文件 | 任意進程 | 文件刪除前持久存在 | 無親緣進程、守護進程通信 |
命名管道(FIFO)是「有名字的管道」,以文件路徑為會合點,打破了匿名管道「親緣關係」的限制。掌握 mkfifo → open → read/write → close → unlink 標準流程,並理解阻塞 open 語義與同步機制,即可靈活實現進程間可靠數據傳輸。