管道是 Unix/Linux 系統中最經典的進程間通訊 (IPC) 機制之一,提供單向資料流,配合 fork() 實現父子進程協作。本文深入解析 pipe() 系統呼叫、檔案描述符機制、讀寫同步與完整程式碼實作。
#include
int pipe(int file_descriptor[2]);
fd[2],回傳兩個檔案描述符errnopipe() 建立管道,獲得 fd[0]、fd[1]fork() 建立子進程,兩進程共享相同檔案描述符表fd[0],透過 write(fd[1], ...) 發送資料fd[1],透過 read(fd[0], ...) 接收資料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 (因寫入端仍開啟)read():管道無資料時阻塞,直到有資料或寫入端關閉write():管道緩衝區滿時阻塞 (預設 64KB),直到有空間wait():父進程同步等待子進程退出,避免殭屍進程cat /proc/sys/fs/pipe-max-size (通常 65536 位元組)PIPE_BUF (POSIX 保證 ≥ 512 位元組) 內寫入為**原子操作**,多進程並發寫入不會交錯適用於**無親緣關係進程**間通訊:
// 建立命名管道
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) |