C语言读取已满命名管道:确定再次写入前需读取的字节数
命名管道(FIFO)容量检测及写入恢复问题排查
问题背景
作业要求:
- 检查命名管道(FIFO)的容量;
- 确定从已满的管道中需要读取多少字节后才能再次写入。
当前代码可成功写入数据直至管道满,显示其容量为65536字节,但后续读取操作失败(返回值为-1,错误码为Resource temporarily unavailable),且出现未读取任何数据就成功写入的情况,输出Write successful after reading 0 bytes。
问题代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> int main() { int l = 0; int stream; char* fifo = "/tmp/fifo"; mkfifo(fifo, 0666); char test = 255; int p = fork(); char readtest; if (p > 0) { // 父进程 wait(NULL); stream = open(fifo, O_RDWR | O_NONBLOCK); while (1) { if (read(stream, &readtest, 1) == 1) { l++; printf("Read byte %d, attempting write\n", l); } else { printf("Read failed, errno: %d (%s)\n", errno, strerror(errno)); } if (write(stream, &test, 1) == 1) { printf("Write successful after reading %d bytes\n", l); return 0; } } } else if (p == 0) { // 子进程 stream = open(fifo, O_RDWR | O_NONBLOCK); while (write(stream, &test, 1) == 1 ) { l++; } close(stream); printf("Wrote %d bytes, pipe full\n", l); _Exit(0); } return 0; }
问题分析
- 管道数据被清空:父进程调用
wait(NULL)等待子进程退出,子进程写完后关闭了FIFO的所有读写端(因以O_RDWR打开)。此时无任何进程持有管道的端,内核会自动清空缓冲区数据。父进程后续打开FIFO时管道为空,写入操作直接成功,导致Write successful after reading 0 bytes的异常输出。 - 非阻塞读取失败:父进程以
O_NONBLOCK模式打开空管道,调用read时因无数据可读且是非阻塞模式,返回-1并设置errno为EAGAIN(对应错误信息Resource temporarily unavailable)。
解决方案
要保留管道数据并正确测试读取后写入的条件,需保证管道始终有至少一个端处于打开状态,避免数据被清空。调整后的代码如下:
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <string.h> int main() { int l = 0; int parent_read_fd, child_write_fd, parent_write_fd; char* fifo = "/tmp/fifo"; unlink(fifo); // 清理已有FIFO,避免创建失败 mkfifo(fifo, 0666); char test = 255; int p = fork(); char readtest; if (p > 0) { // 父进程 // 先打开读端,保持管道读端存在 parent_read_fd = open(fifo, O_RDONLY); wait(NULL); // 等待子进程写满管道 // 打开写端用于测试写入 parent_write_fd = open(fifo, O_WRONLY | O_NONBLOCK); while (1) { ssize_t read_ret = read(parent_read_fd, &readtest, 1); if (read_ret == 1) { l++; printf("Read byte %d, attempting write\n", l); } else if (read_ret == 0) { printf("Pipe EOF reached\n"); break; } else { if (errno != EAGAIN) { printf("Read error: %s\n", strerror(errno)); break; } } // 尝试写入 ssize_t write_ret = write(parent_write_fd, &test, 1); if (write_ret == 1) { printf("Write successful after reading %d bytes\n", l); break; } else if (errno != EAGAIN) { printf("Write error: %s\n", strerror(errno)); break; } } close(parent_read_fd); close(parent_write_fd); unlink(fifo); // 清理FIFO } else if (p == 0) { // 子进程 child_write_fd = open(fifo, O_WRONLY); while (write(child_write_fd, &test, 1) == 1 ) { l++; } printf("Wrote %d bytes, pipe full\n", l); sleep(2); // 等待父进程完成读取测试 close(child_write_fd); _Exit(0); } return 0; }
调整说明
- 保持管道端打开:父进程提前打开读端,子进程写完后不立即关闭写端,确保管道数据不会被内核清空。
- 分离读写端:父进程分别打开读端和写端,避免
O_RDWR模式导致的自我连接问题,更符合FIFO的典型使用场景。 - 完善错误处理:添加对
EAGAIN错误的判断,区分非阻塞模式下的正常状态与真正的读写错误。 - 清理残留文件:运行前后清理FIFO文件,避免因残留文件导致的异常。
内容的提问来源于stack exchange,提问作者crepper4454
相关产品推荐
相关产品推荐

