并发fwrite写入WekaFS时read读取全0异常问题排查
实时读写WekaFS文件时读取缓冲区全0的问题分析与解决
问题描述
我有两个进程:一个通过fwrite循环写入文件,另一个通过read循环实时读取该文件。读取进程使用O_DIRECT标志打开文件,偶尔会出现read()返回正的读取字节数,但缓冲区内容全为0x00的情况,重试读取后即可恢复正常。文件存储在WekaFS网络文件系统上,读取操作在写入进程持续写入时实时进行。
读取进程代码如下:
#include <iostream> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main() { const char* filename = "<my file>"; // Open the file for reading int fd = open(filename, O_DIRECT | O_RDONLY); if (fd < 0) { perror("Failed to open file for reading"); return 1; } size_t sz = 40000; char *read_data; if (posix_memalign((void**)&read_data, 512, sz) != 0) { perror("Failed to allocate aligned memory"); return 1; } bool loop = true; size_t total_bytes = 0; while (loop) { ssize_t bytes_read = read(fd, read_data, sz); if (bytes_read < 0) { perror("Failed to read from file"); break; } else if (bytes_read == 0) { std::cout << "End of file reached." << std::endl; break; } else { total_bytes += bytes_read; std::cout << "Read " << bytes_read << " bytes, total so far: " << total_bytes << std::endl; } } // Clean up free(read_data); close(fd); return 0; }
问题原因分析
这个问题和网络文件系统的一致性机制、读写同步缺失都有关系:
- O_DIRECT的特性与WekaFS的延迟同步:
O_DIRECT绕过了操作系统页缓存,直接和存储设备交互。但WekaFS作为分布式文件系统,写入进程的fwrite返回后,数据可能还在同步到存储节点的过程中,此时用O_DIRECT读取会拿到未填充数据的块(默认用0填充),重试时数据已完成同步,就能读到正确内容。 - 无同步的异步读写:当前读写进程没有任何同步机制,写入操作和读取操作完全独立,无法保证读取时目标区域的写入已经完成持久化。
- WekaFS的并发读写处理:部分分布式文件系统为了性能,会对未完全持久化的块返回占位数据(比如0),直到数据完成副本同步或持久化。
解决方案
1. 强化写入进程的同步策略
在写入进程的fwrite之后,强制刷写数据到存储设备,确保写入完成后数据才对读取进程可见:
// 写入进程中每次fwrite后添加 fwrite(buffer, size, count, fp); fflush(fp); // 刷新用户空间缓存到内核 fsync(fileno(fp));// 确保内核数据刷到WekaFS存储节点
2. 调整读取进程的打开标志
如果业务允许使用操作系统页缓存,可以移除O_DIRECT,让内核自动处理缓存一致性:
// 去掉O_DIRECT标志 int fd = open(filename, O_RDONLY);
页缓存会自动维护读写的可见性,大幅减少未同步导致的全0问题。如果必须保留O_DIRECT,则需要在读取前确保目标区域的数据已经完成写入同步。
3. 增加读写进程的同步机制
通过文件锁实现读写互斥,确保读取时写入操作已经完成:
- 读取进程使用共享锁(
F_RDLCK) - 写入进程使用独占锁(
F_WRLCK)
读取进程修改示例:
// 在read前添加锁逻辑 struct flock lock; lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; // 锁定整个文件 lock.l_pid = getpid(); // 阻塞等待获取共享锁 if (fcntl(fd, F_SETLKW, &lock) == -1) { perror("Failed to acquire read lock"); // 错误处理 } ssize_t bytes_read = read(fd, read_data, sz); // 释放锁 lock.l_type = F_UNLCK; if (fcntl(fd, F_SETLK, &lock) == -1) { perror("Failed to release lock"); }
4. WekaFS专属优化
查看WekaFS官方文档,调整文件的一致性级别(比如启用强一致性模式),或配置客户端缓存策略,确保读取请求能获取到最新的已持久化数据。
内容的提问来源于stack exchange,提问作者rishi jain
相关产品推荐
相关产品推荐

