我有两个进程:一个是使用 fwrite 循环写入文件,另一个是使用 read 循环读取文件。读取器进程有时会返回零字节(缓冲区中全部为零),但是当它尝试再次读取时,它工作正常。该文件位于网络文件系统 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;
}
问题: 有时,当读取器尝试读取时,它会在缓冲区中得到全零,我认为这是垃圾数据。当读者再次尝试时,效果很好。 写入器不断写入文件,读取器在文件被修改的同时实时读取文件(实时读取)。 该文件位于 wekafs 网络文件系统上,我不确定问题是否与网络延迟或文件系统的缓存问题有关。
我尝试过的: 确保阅读器正确检查错误(即检查 read() 返回值)。
问题是否与网络文件系统如何处理实时读写有关,或者我在处理读写器同步时还遗漏了什么?
这是预期的行为,至少在 Unix 下是这样。文件上的
read
函数(管道和其他源有不同的行为)是立即返回,将所有可用字节从当前位置传输到文件的当前末尾,并给出最大计数。如果当前读取位置对应于当前文件末尾,read
将传输 0 个字节并返回 0,即使另一个程序稍后可能会写入其他字节,从而更改当前文件末尾。当然,在大多数情况下,读取文件的程序并不期望其他程序同时写入该文件,并且会将读取 0 字节视为文件结尾,但这是由应用程序和大多数库——例如,在这种情况下,std::ifstream
将关闭文件并发出 EOF 信号。但这是应用程序或库的决定,有些程序,例如tail -f
,即使读取了0个字节也会继续尝试读取。
因此,当编写者不会写任何其他内容时,您需要定义一些其他方法来检测真正的文件结尾。这可以通过哨兵值(例如“” EOF ") 在输入中,一些带外信号(例如发送到读取进程的信号)或简单的超时 - 如果写入进程在过去 60 秒内没有写入任何内容,比如说,我们关闭文件并在所有情况下,在读取 0 字节后重试读取之前稍微休息一下可能是个好主意。
请注意,我不太确定
O_DIRECT
在这里会产生什么影响。我希望读取的字节数始终是磁盘块大小的倍数(可能是 0),但 Linux 文档并不是很清楚——它只说它将“尝试最小化缓存影响”,所以目前尚不清楚如果编写程序不使用 O_DIRECT
会发生什么。我相当怀疑,除非编写程序也使用O_DIRECT
,否则这是不合适的。