FIFO管道读取最大元素数限制问题求助
我尝试编写一个简单的读写程序,使用FIFO管道进行通信。
Reader代码:
#include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFFSIZE 65600 //921600 // 640*480*3 #define err(mess) { fprintf(stderr,"Error: %s.", mess); exit(1); } int main() { int fd; ssize_t n; char buf[BUFFSIZE]; long int i; if ( (fd = open("./shared/test", O_RDONLY)) < 0) err("open") while( (n = read(fd, buf, BUFFSIZE) ) > 0) { printf("Read (%d): ", n); for (i=0; i<BUFFSIZE; i++) printf("%c", buf[i]); printf("\n"); } close(fd); return 0; }Writer代码:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define BUFFSIZE 65600 //921600 // 640*480*3 #define err(mess) { fprintf(stderr,"Error: %s.", mess); exit(1); } int main() { int fd; char buf[BUFFSIZE]; long int i, j; mkfifo("./shared/test", 0666); if ( (fd = open("./shared/test", O_WRONLY)) < 0) err("open") // main loop j=0; while (1){ for (i=0;i<BUFFSIZE;i++){ buf[i]= j + '0'; } j++; buf[BUFFSIZE-1]=j + '0'; write(fd, buf, sizeof(buf)); sleep(10); } close(fd); return 0; }当
BUFFSIZE设置为65535(2^16-1)时程序能正常工作,但改成更大的数值(比如65600)后,读取时会出现类似这样的情况:Read (65536): 0000000000000000000000000000000000000,后面跟着一堆垃圾数据。请问如何让程序支持更大的元素数量?谢谢!
—— Joe
问题根源分析
这个问题的核心是FIFO管道的默认缓冲区大小限制。大多数系统中,管道(包括FIFO)的默认缓冲区大小是64KB(也就是65536字节)。当你尝试一次性写入超过这个大小的数据时,write调用会被阻塞直到有部分数据被读取,但你的读取逻辑存在两个关键问题:
读取长度处理错误:你在
printf("Read (%d): ", n);中把ssize_t类型的n用%d格式化,更直接的问题是,read实际只能返回当前管道中可用的数据量——也就是默认的64KB,剩下的数据还在写入等待中,但你的代码却直接遍历整个BUFFSIZE长度的缓冲区,这就导致未被填充的部分输出垃圾数据。循环读取不完整:你的读取逻辑只做了一次
read就遍历整个缓冲区,但实际上当写入的数据超过管道缓冲区大小时,需要多次调用read才能把所有数据读完。
修正方案
1. 修复Reader的读取逻辑
需要循环读取直到获取到完整的BUFFSIZE字节数据,或者处理到文件结束。修改后的Reader代码如下:
#include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define BUFFSIZE 65600 //921600 // 640*480*3 #define err(mess) { fprintf(stderr,"Error: %s.", mess); exit(1); } int main() { int fd; ssize_t n, total_read; char buf[BUFFSIZE]; long int i; if ( (fd = open("./shared/test", O_RDONLY)) < 0) err("open") while(1) { total_read = 0; // 循环读取直到拿到完整的BUFFSIZE数据 while (total_read < BUFFSIZE) { n = read(fd, buf + total_read, BUFFSIZE - total_read); if (n <= 0) { if (n == 0) { // 写入端关闭,退出 close(fd); return 0; } else { err("read"); } } total_read += n; } printf("Read (%zd): ", total_read); for (i=0; i<BUFFSIZE; i++) printf("%c", buf[i]); printf("\n"); } close(fd); return 0; }
2. 可选:调整FIFO缓冲区大小
如果你希望减少读取次数,可以通过fcntl系统调用调整FIFO的缓冲区大小(注意:这个操作需要系统支持,不同系统的上限不同)。在Writer打开FIFO后添加以下代码:
// 调整缓冲区大小为128KB(示例值,根据需求修改) int pipe_size = 131072; if (fcntl(fd, F_SETPIPE_SZ, pipe_size) == -1) { perror("fcntl F_SETPIPE_SZ failed"); // 调整失败不终止程序,继续使用默认大小 }
额外注意事项
- Writer的错误处理:当前Writer中的
mkfifo调用没有错误处理,如果FIFO已经存在会报错,建议添加判断:if (mkfifo("./shared/test", 0666) == -1) { if (errno != EEXIST) { err("mkfifo"); } } - 数据类型匹配:
ssize_t对应的格式化符号是%zd,不要用%d,避免类型转换错误。
这样修改后,程序就能支持更大的BUFFSIZE值了。
备注:内容来源于stack exchange,提问作者user3318590

