繁体   English   中英

为什么对内存映射零字节文件的读操作会导致SIGBUS?

[英]Why does a read operation on a memory mapped zero byte file lead to SIGBUS?

这是我写的示例代码。

#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吗?

当访问过去整个映射页面的末尾时,您会收到SIGBUS ,因为POSIX标准指出

mmap()函数可用于映射大于对象当前大小的内存区域。 映射中的内存访问但超出底层对象的当前末尾可能导致SIGBUS信号被发送到进程。

使用零字节文件,您映射的整个页面“超出了底层对象的当前末尾”。 所以你得到了SIGBUS

当你超越你绘制的4kB页面时,你不会得到一个SIGBUS ,因为那不在你的映射中。 当文件大于零字节时,您不会获得SIGBUS访问映射,因为整个页面都已映射。

但是,如果您将其他页面映射到文件末尾,则会得到一个SIGBUS ,例如为一个1字节的文件映射两个 4kB页面。 如果您访问第二个4kB页面,您将获得SIGBUS

1字节文件不会导致崩溃,因为mmap将以页面大小的倍数映射内存,并将剩余部分归零。 从手册页:

文件以页面大小的倍数映射。 对于不是页面大小倍数的文件,剩余内存在映射时归零,并且写入该区域不会写入文件。 未指定更改对应于文件的添加或移除区域的页面上的映射的基础文件的大小的效果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM