管道(Pipe)是Unix/Linux中最古老的进程间通信(IPC)方式,提供单向数据流。
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd[0]:读端文件描述符pipefd[1]:写端文件描述符#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
// 子进程:写入数据
close(pipefd[0]); // 关闭读端
const char *msg = "Hello from child process!";
write(pipefd[1], msg, strlen(msg) + 1);
close(pipefd[1]);
exit(0);
} else {
// 父进程:读取数据
close(pipefd[1]); // 关闭写端
char buf[1024];
ssize_t n = read(pipefd[0], buf, sizeof(buf));
if (n > 0) {
printf("Parent received: %s\n", buf);
}
close(pipefd[0]);
wait(NULL);
}
return 0;
}
编译命令:
gcc -o pipe_demo pipe_demo.c
./pipe_demo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int pipe1[2], pipe2[2]; // pipe1: 父->子, pipe2: 子->父
pipe(pipe1);
pipe(pipe2);
pid_t pid = fork();
if (pid == 0) {
// 子进程
close(pipe1[1]); // 关闭pipe1写端
close(pipe2[0]); // 关闭pipe2读端
char buf[1024];
read(pipe1[0], buf, sizeof(buf));
printf("Child received: %s\n", buf);
const char *reply = "Hello from child!";
write(pipe2[1], reply, strlen(reply) + 1);
close(pipe1[0]);
close(pipe2[1]);
exit(0);
} else {
// 父进程
close(pipe1[0]); // 关闭pipe1读端
close(pipe2[1]); // 关闭pipe2写端
const char *msg = "Hello from parent!";
write(pipe1[1], msg, strlen(msg) + 1);
char buf[1024];
read(pipe2[0], buf, sizeof(buf));
printf("Parent received: %s\n", buf);
close(pipe1[1]);
close(pipe2[0]);
wait(NULL);
}
return 0;
}
编译命令:
gcc -o pipe双向通信 pipe双向通信.c
./pipe双向通信
PIPE_BUF(Linux默认4096字节)时,保证原子性PIPE_BUF可能被拆分| 场景 | 行为 |
|---|---|
| 管道空,读阻塞 | read阻塞等待数据 |
| 管道满,写阻塞 | write阻塞等待空间 |
| 读端关闭,写入 | 收到SIGPIPE信号,write返回EPIPE |
| 写端关闭,读取 | read返回0(EOF) |
| 非阻塞读(O_NONBLOCK) | 管道空返回-1,errno=EAGAIN |
| 非阻塞写(O_NONBLOCK) | 管道满返回-1,errno=EAGAIN |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main() {
int pipefd[2];
pipe(pipefd);
// 设置非阻塞
fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
char buf[1024];
ssize_t n = read(pipefd[0], buf, sizeof(buf));
if (n == -1 && errno == EAGAIN) {
printf("Pipe is empty (non-blocking read)\n");
}
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
编译命令:
gcc -o pipe_nonblock pipe_nonblock.c
./pipe_nonblock
fork后子进程继承父进程的文件描述符表,父子进程共享管道的读写端。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == 0) {
// 子进程关闭读端,只写
close(pipefd[0]);
write(pipefd[1], "child", 5);
close(pipefd[1]);
exit(0);
} else {
// 父进程关闭写端,只读
close(pipefd[1]);
char buf[1024];
read(pipefd[0], buf, sizeof(buf));
printf("Received: %s\n", buf);
close(pipefd[0]);
wait(NULL);
}
return 0;
}
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
pathname:管道文件路径mode:文件权限// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
mkfifo("/tmp/myfifo", 0666);
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "Hello FIFO!", 12);
close(fd);
return 0;
}
// reader.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/tmp/myfifo", O_RDONLY);
char buf[1024];
read(fd, buf, sizeof(buf));
printf("Received: %s\n", buf);
close(fd);
return 0;
}
编译命令:
gcc -o writer writer.c
gcc -o reader reader.c
# 终端1运行
./writer
# 终端2运行
./reader
// 传感器数据采集进程 -> 数据处理进程
// 通过管道传递采集数据
cat sensor_data.log | grep "temperature" | awk '{print $2}'
# 每个|创建一个管道,进程间通过管道通信
// 嵌入式设备将日志通过管道传给串口打印程序
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == 0) {
// 子进程:读管道,写串口
dup2(pipefd[0], STDIN_FILENO);
execl("/bin/cat", "cat", "/dev/ttyS0", NULL);
}
// 父进程:写管道
write(pipefd[1], log_data, len);