Linux进程间通信:管道读取数据丢失问题排查求助
管道通信数据丢失问题分析与修复
你的代码出现数据丢失的核心原因是子进程错误写入了整个缓冲区大小,而非实际生成的字符串长度,再结合管道的字节流特性,导致父进程读取时无法正确解析所有数据。
具体问题点
在子进程的循环中,你使用了:
write(link[1], buf_child, sizeof(buf_child));
这里sizeof(buf_child)是256,但sprintf生成的字符串(比如current number is : 0)实际长度仅20字节左右,剩余230多字节是缓冲区里的未初始化垃圾数据(包含之前循环的残留内容)。
同时,管道是面向字节流的,内核会把连续的小写入操作合并成一个大的读取块:
- 比如父进程第一次
read会读取1024字节(刚好是4个256字节的写入块),缓冲区里拼接了第0、1、2、3次写入的内容 - 但你用
%.*s打印时,会在第一个\0(第0次写入的字符串结尾)处停止,后面第1-3次的有效数据被完全截断,导致看不到这些内容
添加sleep(1)后,子进程每次写入后暂停,父进程有足够时间单独读取每个256字节块,此时每个读取操作只包含一次写入的内容,%.*s能正确打印到对应字符串的\0,因此能看到全部10次输出。
修复后的代码
修改子进程中write的长度参数,使用实际生成的字符串长度(包含终止符\0),同时调整父进程的打印逻辑以处理合并的写入块:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <errno.h> #include <string.h> int main(int argc, char* argv[]) { pid_t pid; char buf[1024]; int link[2]; pipe(link); pid = fork(); if (pid < 0) { printf("fork failed\n"); return -1; } else if(pid == 0) { //child process char buf_child[256]; close(link[0]); int i; for(i = 0; i < 10; ++i){ sprintf(buf_child,"current number is : %d", i); // 写入实际字符串长度(包含'\0') write(link[1], buf_child, strlen(buf_child) + 1); // sleep(1); } printf("[child process] now close pipe write end\n"); close(link[1]); printf("child process exit...\n"); exit(0); } else {//parent process close(link[1]); int nbytes; while(1) { printf("[parent process] start read...\n"); nbytes = read(link[0], buf, sizeof(buf)); if(nbytes == -1) { printf("read failed, reason: %s\n", strerror(errno)); break; } else if(nbytes != 0){ // 遍历读取到的所有字符串,处理合并的写入块 char* ptr = buf; while (ptr < buf + nbytes) { printf("data: %s\n", ptr); ptr += strlen(ptr) + 1; } } else { printf("parent process: finish read from pipe\n"); break; } } wait(NULL); } return 0; }
额外说明
- 如果你不需要传递字符串终止符
\0,也可以只写入strlen(buf_child)字节,但父进程需要自行处理消息分割(比如约定固定长度、或使用换行符作为分隔符)。 - 管道的字节流特性意味着没有天然的消息边界,若需要严格区分每次写入的消息,除了上述方法,还可以改用面向消息的IPC机制(如消息队列)。
内容的提问来源于stack exchange,提问作者Gilbert Tian
相关产品推荐
相关产品推荐

