【发布时间】:2017-05-15 22:09:11
【问题描述】:
这是我写的示例代码。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main()
{
int fd;
long pagesize;
char *data;
if ((fd = open("foo.txt", O_RDONLY)) == -1) {
perror("open");
return 1;
}
pagesize = sysconf(_SC_PAGESIZE);
printf("pagesize: %ld\n", pagesize);
data = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
printf("data: %p\n", data);
if (data == (void *) -1) {
perror("mmap");
return 1;
}
printf("%d\n", data[0]);
printf("%d\n", data[1]);
printf("%d\n", data[2]);
printf("%d\n", data[4096]);
printf("%d\n", data[4097]);
printf("%d\n", data[4098]);
return 0;
}
如果我向这个程序提供一个零字节的 foo.txt,它会以 SIGBUS 终止。
$ > foo.txt && gcc foo.c && ./a.out
pagesize: 4096
data: 0x7f8d882ab000
Bus error
如果我向这个程序提供一个单字节的 foo.txt,那么就不存在这样的问题。
$ printf A > foo.txt && gcc foo.c && ./a.out
pagesize: 4096
data: 0x7f5f3b679000
65
0
0
48
56
10
mmap(2) 提到以下内容。
使用映射区域可能会产生以下信号:
SIGSEGV 尝试写入映射为只读的区域。
SIGBUS 尝试访问与文件不对应的缓冲区部分(例如,超出文件末尾,包括另一个进程截断文件的情况)。
所以如果我理解正确,即使是第二个测试用例(1 字节文件)也应该导致 SIGBUS,因为 data[1] 和 data[2] 正在尝试访问缓冲区的一部分(data)与文件不对应。
你能帮我理解为什么只有一个零字节的文件会导致这个程序因 SIGBUS 而失败吗?
【问题讨论】:
-
你为什么在乎?您调用未定义的行为;任何事情都可能发生。甚至不能保证你会收到任何信号。
-
POSIX 是基于 C 标准的,对于 C 标准,这种访问显然是 UB。说,不清楚你的问题是什么。你的代码无论如何都坏了,如果你得到一个 SIGBUS 或 SIGSEGV 或类似的,你已经把事情搞砸了。但这不是双射的:如果你没有收到信号,并不意味着你的代码是正确的!
-
@Olaf 我本身没有任何问题。出于好奇,我有一个问题。这个问题可以概括为:Linux 上的
man页面表明,任何对与文件不对应的缓冲区部分的访问(例如,超出文件末尾)都应该导致SIGBUS。但我的第二个测试似乎与man页面在同一 Linux 系统上所说的相矛盾。 -
提出问题的完全正当理由,应该鼓励好奇心。我不认为说问题不值得问是有建设性的。
-
访问映射的页面,其中整个页面超出映射文件的末尾,似乎不是 UB。 POSIX standard for
mmap明确指出允许大于底层文件的映射,但访问这样的页面会导致SIGBUS:mmap()函数可用于映射大于对象的当前大小。映射内但超出底层对象当前端的内存访问可能导致SIGBUS信号被发送到进程。