C语言父进程周期性向子进程管道写数据的输出异常排查
问题原因分析与修复方案
这是个很典型的stdio缓冲问题,另外你的代码里还有一个容易忽略的内存错误,咱们一步步说清楚:
1. 未初始化的read_msg导致未定义行为
你声明了char *read_msg;但没有给它分配内存,也没有让它指向一个有效的char变量。当调用read(fd[READ_END], read_msg, 1)时,你是在往随机内存地址写入数据,这会触发未定义行为——程序可能崩溃、输出乱码,或者表现出完全不符合预期的结果。
修复这个问题很简单,把指针改成一个普通的char变量,读取时传入它的地址:
// 把 char *read_msg; 改成: char read_msg; // 读取时用 &read_msg 获取地址 while (read(fd[READ_END], &read_msg, 1)) { printf("%c", read_msg); }
2. stdout行缓冲导致输出延迟
这才是你看到“等待数秒后一次性输出”的核心原因:
- 默认情况下,当stdout连接到终端时,它处于行缓冲模式。也就是说,只有当输出内容包含换行符
\n、缓冲区被填满,或者程序即将退出时,缓冲区里的内容才会被真正刷新到屏幕上。 - 你的子进程中,一开始打印
"Read: "没有换行,之后每次读取字符后打印单个字符也没有换行——这些内容全部被存在stdout的缓冲区里,没有立刻显示。直到父进程写完所有字符、关闭管道,子进程的read返回0,执行printf("\n");时,换行符触发了缓冲区刷新,所有攒下来的内容才一次性输出。
修复输出延迟的几种办法
你可以根据需求选择其中一种:
- 每次打印后手动刷新缓冲区:在单个字符的printf后调用
fflush(stdout),强制把缓冲区内容刷到屏幕:while (read(fd[READ_END], &read_msg, 1)) { printf("%c", read_msg); fflush(stdout); // 手动刷新 } - 关闭stdout的缓冲:在子进程开头添加
setvbuf调用,将stdout设置为无缓冲模式,任何输出都会立刻显示:if (pid == 0) { close(fd[WRITE_END]); setvbuf(stdout, NULL, _IONBF, 0); // 关闭缓冲 char read_msg; printf("Read: "); while (read(fd[READ_END], &read_msg, 1)) { printf("%c", read_msg); } printf("\n"); close(fd[READ_END]); } - 如果允许换行的话,也可以每次输出后加换行,但这显然不符合你“在一行逐个输出字符”的需求,所以前面两种更合适。
修复后的完整代码示例
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #define WRITE_END 1 #define READ_END 0 int main() { int fd[2]; pipe(fd); pid_t pid = fork(); if (pid == 0) { close(fd[WRITE_END]); setvbuf(stdout, NULL, _IONBF, 0); // 关闭stdout缓冲 char read_msg; printf("Read: "); // Read characters. while (read(fd[READ_END], &read_msg, 1)) { printf("%c", read_msg); } printf("\n"); close(fd[READ_END]); } else { close(fd[READ_END]); char *msg = "Hello!"; char *c = msg; // Write a character from parent every second. while (*c) { write(fd[WRITE_END], c++, 1); sleep(1); } close(fd[WRITE_END]); wait(NULL); } return 0; }
内容的提问来源于stack exchange,提问作者Emily Horsman
相关产品推荐
相关产品推荐

