匿名管道 pipe() 是父子進程通信的標準方式:內核創建一對文件描述符 fd[0](讀端)、fd[1](寫端),fork() 後雙方繼承描述符,關閉各自不用的一端,形成單向數據流。本文給出標準「創建→fork→關閉無用端→讀寫→同步」完整流程代碼。
#includeint pipe(int fd[2]); // 成功返回 0,fd[0]=讀端,fd[1]=寫端 // 失敗返回 -1,設置 errno
pipe(fd) 創建管道。fork() 創建子進程,父子雙方擁有相同 fd[0], fd[1]。fd[1](寫端),子進程關閉 fd[0](讀端),避免死鎖與 EOF 誤判。write(fd[1], ...) 發送數據。read(fd[0], ...) 接收數據。wait() 同步等待子進程結束(可選,視邏輯)。#include#include #include #include #include #define BUFSIZ 1024 int main() { char input[] = "IPC by pipe"; char output[BUFSIZ+1] = {0}; int fd[2]; int count = 0; pid_t pid, pid_res; int stat_value; // 1. 創建管道 if (pipe(fd) == -1) { perror("pipe"); return 1; } // 2. 創建子進程 pid = fork(); if (pid < 0) { perror("fork"); return 1; } if (pid > 0) { // 父進程:讀取 close(fd[1]); // 關閉寫端,關鍵! pid_res = wait(&stat_value); // 等待子進程寫入結束 if (pid_res > 0) { count = read(fd[0], output, BUFSIZ); printf("Father read %d bytes: %s\n", count, output); close(fd[0]); } } else { // 子進程:寫入 close(fd[0]); // 關閉讀端,關鍵! count = write(fd[1], input, strlen(input)); printf("Son wrote %d bytes: %s\n", count, input); close(fd[1]); exit(0); } return 0; }
Son wrote 12 bytes: IPC by pipe Father read 12 bytes: IPC by pipe
fd[1],自己也持有寫端,導致管道永遠有寫端打開,read() 永遠讀不到 EOF(返回 0),會無限阻塞。fd[0],雖不影響寫入,但浪費描述符,且若父進程異常退出未關閉讀端,子進程寫入可能觸發 SIGPIPE。父進程調用 wait() 阻塞,直到子進程 exit()。這保證:
write 並 close(fd[1]) 發送 EOF。read 讀到數據後再繼續,避免競態。| 錯誤代碼 | 後果 | 修正 |
|---|---|---|
| 父子均不關閉無用端 | read 永不返回 EOF,死鎖 | 各關閉自己不用的端 |
| 父進程先 read 再 wait | 若子進程寫慢,read 阻塞正常;但無 wait 會產生殭屍進程 | 保留 wait 或用 SIGCHLD 處理 |
| 缓衝區大小不足 | write 可能阻塞或部分寫入 | 循環 write 直到全部寫完 |
匿名管道 pipe() 是父子進程通信的輕量級標準方案:
pipe(fd) 一對描述符。fork() 共享描述符。write/read 數據流。wait() 同步清理。掌握這套「管道六步法」,即可構建高性能、無競態的父子進程數據通道。