POSIX 系统上的内存映射文件保持同步
为什么下面的代码可以正确运行?
void continuous_mmap (void)
{
struct stat buf;
int fd = open("file_one", O_RDONLY), i;
char *contents;
fstat(fd, &buf);
contents = mmap(NULL, buf.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
close (fd);
mprotect(contents, buf.st_size, PROT_READ);
for (i = 0; i < 15; i++) {
printf ("%s\n", contents);
sleep (1);
}
munmap(contents, buf.st_size);
}
首先,即使附加文件,文件也保持同步(在外部编辑和保存文件会自动打印更新的内容)。我的代码如何能够访问超出我映射的字节数(初始文件大小)而不会出现段错误?是因为 mmap
总是将长度四舍五入到系统页面大小吗?如果是这样,这种行为是否可以依赖于一般的 POSIX 系统(我在 mmap
手册页中找不到任何此类要求)。
其次,文本如何自动附加 '0'
?是因为非映射字节自动归零吗?这种行为可以依赖吗?
Why does the following code work correctly?
void continuous_mmap (void)
{
struct stat buf;
int fd = open("file_one", O_RDONLY), i;
char *contents;
fstat(fd, &buf);
contents = mmap(NULL, buf.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
close (fd);
mprotect(contents, buf.st_size, PROT_READ);
for (i = 0; i < 15; i++) {
printf ("%s\n", contents);
sleep (1);
}
munmap(contents, buf.st_size);
}
Firstly, the file stays in sync (editing and saving the file externally automatically prints the updated contents), even when appended to. How is my code able to access beyond the number of bytes I've mapped (the initial file size) without segfaulting? Is it because mmap
always rounds up the length to the system page size? If so, can this behavior be depended upon on POSIX
systems in general (I could not find any such requirement in the mmap
man page).
Secondly, how does the text automatically get appended with a '0'
? Is it because the non-mapped bytes are automatically zeroed? Can this behavior be depended upon?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,标准说
一般而言,在 POSIX 系统上(我
找不到任何此类要求
在 mmap 手册页中)。
不,我不会这样做,并非所有实现都如此一致。我至少见过一次相当糟糕的实现。
您不应使用
mmap
调用的此功能,而应使用ftruncate
来根据需要延长文件长度。yes the standard says
upon on POSIX systems in general (I
could not find any such requirement
in the mmap man page).
no, I wouldn't do that, not all implementations may be as conforming. I have seen at least one quite broken implementation, once.
You should not use this feature of the
mmap
call for this butftruncate
to lengthen your file to your needs.POSIX 甚至不强制规定页面大小;理论上,一个实现的“页”大小可以为 1 字节。同样,从超过文件大小的页面剩余部分读取零似乎也没有指定。我可以想象一些损坏的实现会泄露此处被截断的旧文件内容,但我认为这是一个重大的安全/隐私漏洞,这将使这种实现在现实世界中变得无关紧要。当然,他们可以用
0xDEADBEEF
填充该空间,然后你就倒霉了。即使您可以假设零填充(这可能是大多数现实世界操作系统的情况),我也会警告不要使用它。如果您的文件恰好是系统页面大小的倍数,会发生什么?突然,您的代码在读到末尾时崩溃,或者(可能更糟糕)从恰好映射到文件映射附近的不相关页面读取。这是一个非常非常讨厌的错误,您可能无法发现,因为文本文件的大小恰好是系统页面大小的倍数的可能性非常低。
POSIX does not even mandate a nontrivial page size; in theory an implementation could have a "page" size of 1 byte. Similarly, reading zeros from the remainder of the page past the file size seems not to be specified. I could imagine some broken implementations leaking old file contents that were truncated here, but I would consider that a major security/privacy breach that would make such an implementation irrelevant in the real world. Of course they could fill the space with
0xDEADBEEF
and then you'd be out of luck.Even if you could assume zero-padding (which is probably the case for most real-world operating systems), I would caution against ever using it. What happens if your file happens to be an exact multiple of the system page size? Suddenly your code crashes reading past the end, or (perhaps worse) reads from an unrelated page that just happened to get mapped adjacent to your file's mapping. This is a very very nasty bug you'd probably fail to catch because the probability of having a text file that's an exact multiple of system page size is quite low.
即使文件已映射
MAP_PRIVATE
,您仍会看到该文件的外部更新,这是因为您尚未写入映射,因此系统未向您提供该文件的私有副本页数尚未。这种行为是允许的,但不是必需的。如果您让应用程序在循环之前修改
contents[0]
,它将看不到外部更改。The reason that you're seeing external updates to the file, even though it's mapped
MAP_PRIVATE
, is because you haven't written to the mapping so the system hasn't given you a private copy of the file pages yet. This behaviour is allowed but not required.If you have your application modify
contents[0]
before the loop, it will not see the external changes.