我正在Linux中尝试使用内存映射文件,并且对从不同进程映射同一文件并在文件末尾进行写入时的实际操作有疑问。
我用手创建了一个带有vim的文件,并在其中写入了2个字节:
$ cat test_mmap
aa
然后,我编写了2个非常简单的程序。
第一个程序映射文件并修改映射,而没有msync
和munmap
。
writer.c
:
int main(void){
int fd = open("/tmp/test_mmap", O_CREAT | O_RDWR, S_IRWXU);
char *mapped_region = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_WRITE, MAP_SHARED, fd, 0);
mapped_region[0] = '0';
mapped_region[1] = '1';
mapped_region[2] = '2';
mapped_region[3] = '3';
mapped_region[4] = '4';
mapped_region[5] = '5';
}
第二个正在读取映射。
reader.c
:
int main(void){
int fd = open("/tmp/test_mmap", O_CREAT | O_RDWR, S_IRWXU);
char *mapped_region = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_WRITE, MAP_SHARED, fd, 0);
printf("%c\n", mapped_region[0]);
printf("%c\n", mapped_region[1]);
printf("%c\n", mapped_region[2]);
printf("%c\n", mapped_region[3]);
printf("%c\n", mapped_region[4]);
printf("%c\n", mapped_region[5]);
}
所以我跑了
$ ./writer && ./reader && cat /tmp/test/test_mmap
0
1
2
3
4
5
012
这意味着,超出文件末尾写入的任何数据都会在映射中保留一段时间(尽管它不会写到文件中),并且如果另一个进程因此映射了同一区域,则超出写入范围的数据不会被清零,因为在man-page中指定:
文件被映射为页面大小的倍数。对于不是的文件 页面大小的倍数,当 映射,并且对该区域的写入不会写到文件中。
运行perf -e major-faults ./reader
的阅读器表明
0 major-faults
表示没有从磁盘读取任何页面。同样查看/proc/<pid_writer>/smaps
时,我发现该页面被标记为脏页和私有(即使映射是使用MAP_SHARED
标志创建的):
7fc80f279000-7fc80f27a000 -w-s 00000000 fd:00 6057290 /tmp/test_mmap
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4 kB
如果我在一段时间后运行阅读器进程(需要等待什么时间?),我会观察到
$ ./reader
0
1
2
问题: 是否正确并记录在某处,如果一个进程修改了文件末尾以外的映射,则该页面被标记为脏且只要该页面是脏,另一个进程映射同一文件的同一区域,那么该进程之前写入的数据是否不会被归零并保留一段时间?
答案 0 :(得分:1)
在这些问题上的权威参考是POSIX,它在mmap的理论部分必须说:
mmap()函数可用于映射内存区域 大于对象的当前大小。 [...剪断讨论 在可能的情况下,即在访问结束后的页面时发送SIGBUS 的文件...] 写入的数据可能会丢失,而读取的数据可能不会 反映对象中的实际数据。
因此,POSIX说,这样做可能会导致数据丢失。此外,可移植性充其量也是个问题(想想无MMU系统,与大页面的交互,具有不同页面大小的平台...)