在英特尔(i7)和手臂上映射不同的行为?

Posted

技术标签:

【中文标题】在英特尔(i7)和手臂上映射不同的行为?【英文标题】:mmap different behavior on intel (i7) and arm? 【发布时间】:2014-02-18 14:16:31 【问题描述】:

我使用在带有英特尔 i7 处理器的 Opensuse 13.1 上编译的程序。我在 qemu(虚拟)环境中编译了相同的程序来模拟带有 arm 处理器的 OpenSuse 13.1。这行代码:

rvp = mmap(rvp, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fildes, 0);

给了我一个指向 Ram 内存的指针。然而,intel 和 arm 之间的内存大小不同:

在 intel 上,大小是“长度”,与 fildes 指向的文件的大小无关。

在 ARM 上,大小(大约)是 fildes 指向的文件的大小,忽略了 size 大于 fildes 的事实。

我想分配比文件更多的内存...

编辑:我试图通过两次连续调用来规避这个问题......但没有成功:

    rvp = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    unsigned int i;
    for (i = 0; i < length; i += 5000)
            printf("acces buffer at %i --> %u\n", i, rvp[i]);
    rvp = mmap(rvp, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fildes, 0);
    for (i = 0; i < length; i += 5000)
            printf("acces buffer at %i --> %u\n", i, rvp[i]);

作为输出我得到:

access buffer at 0 --> 0
...
access buffer at 90000 -> 0
access buffer at 0 --> 0
...
access buffer at 85000 --> 0
Segmentation fault...

【问题讨论】:

【参考方案1】:

official specification of mmap在这一点上不是很清楚,但请注意这句话:

[Memory] ​​在地址范围内的引用[,] 从pa 开始并持续到len bytes[,] 到对象末尾之后的整个页面 将导致交付SIGBUS 信号。

我使用了一些逗号并添加了强调以使这一点更清楚:如果您 mmap 的区域大于支持它的文件,则操作系统应该在以下情况下触发信号您尝试访问文件末尾之外的内容,前提是您越过了页面边界(此许可证仅仅是因为硬件不允许操作系统在不落在页面边界的可访问和不可访问内存之间设置边界)(SIGBUSSIGSEGV 现在被许多操作系统视为可互换)。

您的“两个连续调用”方法走在正确的轨道上,但它不起作用,因为您对两个调用使用了相同的长度。如果您在第二次调用中指定了 文件的大小,它会起作用。您可以使用系统调用fstat 检索文件的实际大小。您需要小心一点,以防文件大于您想要的分配长度。我会这样写:

char *map_file(int fd, size_t len)

    struct stat st;
    char *rv;

    if (fstat(fd, &st))
        return report_error("fstat");

    if (st.st_size >= (off_t) len) 
        /* If the file is at least as big as expected, just map the
           chunk we want. */
        rv = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (rv == MAP_FAILED)
            return report_error("mmap");

     else 
        /* Otherwise, we must allocate anonymous memory to fill in the gap. */
        char *anon;
        anon = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
        if (anon == MAP_FAILED)
            return report_error("mmap (anon)");

        rv = mmap(anon, (size_t) st.st_size, PROT_READ|PROT_WRITE,
                  MAP_PRIVATE|MAP_FIXED, fd, 0);
        if (rv == MAP_FAILED) 
            int save_errno = errno;
            (void) munmap(anon, len);
            errno = save_errno;
            return report_error("mmap");
        
    
    return rv;

根据您的更大目标,如果文件没有预期的那么大,则使用ftruncate 放大文件可能是有意义的:

char *map_file(int fd, size_t len)

    struct stat st;
    char *rv;

    if (fstat(fd, &st))
        return report_error("fstat");
    /* if the file isn't as big as expected, make it bigger */
    if (st.st_size < (off_t) len)
        if (ftruncate(fd, (off_t) len))
            return report_error("ftruncate");

    rv = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (rv == MAP_FAILED)
        return report_error("mmap");
    return rv;

但是,如果这是您在上下文中想要的,您可能会使用 MAP_SHARED 而不是 MAP_PRIVATE

(注意,函数 report_error,未显示,记录错误消息,然后返回 NULL。)

【讨论】:

正如我在问题编辑中指出的那样(1 小时前);我尝试了你的第二个建议:两个连续的 mmap 调用......但第二个将可访问内存再次截断为我正在阅读的文件的大小...... 感谢您的帮助,但我已经用另一种方法得到了我想要的东西:rvp = malloc(length); unsigned int nread = read(fildes, rvp, length); @ChrisMaes 是的,这是因为您在两次调用 mmap 时使用了相同的 length 值。如果您在第二次调用中指定了文件大小(由fstat 返回)作为映射长度,它会起作用。【参考方案2】:

好吧,搞砸了一会儿我有答案了:

mmap 应该用于遍历文件,因此分配的内存不会超过我们要访问的文件的大小 我意外访问的文件在我的 intel i7 系统上具有不同的大小,因此它似乎可以在 intel 上运行是有原因的。

--> 解决方案:使用malloc分配大小为“length”的内存,然后使用“read”读取文件...

【讨论】:

以上是关于在英特尔(i7)和手臂上映射不同的行为?的主要内容,如果未能解决你的问题,请参考以下文章

英特尔i7 6700hq和4720hq这两个cpu哪个好啊

电脑中CPU的英特尔酷睿,奔腾和赛扬有啥区别?

第二代智能 英特尔®酷睿™ i7

全新的Skylake 英特尔I7-6700K深度玩评

英特尔CPU是至强系列好还是酷睿系列好?

笔记本参数求鉴定 主板芯片组 Intel HM86 CPU系列 英特尔 酷睿i7 4代系列 CPU型号 Intel 酷睿i7 4700HQ