2

我正在尝试使用 MAP_SHARED 创建内存映射文件。当文件大小达到 2gb 时,我遇到了问题。下面粘贴的代码是我正在使用的(作为测试)。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#define MY_FILE "/dev/shm/mmap_test"
#define BLOCK_SIZE (1024*1024)
#define NUM_FILES 1

void mk_file(int f_num)
{ 
    uint64_t len = 0;
    int fd, j, k;
    char tmp_file[1024], *x, *rr_addr;

    // Open file in /dev/shm
    sprintf(tmp_file, "%s%d", MY_FILE, f_num);
    fd = open(tmp_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

    if (fd == -1)
    {
        perror("file open");
        exit(-1);
    }

    // 16Gb file 
    len = 16UL * 1024 * 1024 * 1024;
    printf("len: %ld Gb\n", len/(1024*1024*1024));

    printf("Mapping %ld blocks\n", len/BLOCK_SIZE);

    for (j = 0; j < len/BLOCK_SIZE; j++) {
        // Increase the file size
        ftruncate(fd, ((j + 1) * BLOCK_SIZE));

        // Just mmap memory... don't have file backing
        //rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0);

        // MMAP a region to the file at a given offset
        rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (j * BLOCK_SIZE));

        if (rr_addr == MAP_FAILED) {
            perror("mmap error");
            printf("INDEX: %d\n", j);
            exit(-1);
        }

        // Write to every byte of allocated memory
        x = (char *) rr_addr;

        for (k = 0; k < BLOCK_SIZE; k++)
        {
            *x = '1';
            x++;
        }
    }

    return;
}

int main(int argc, char **argv)
{
    uint64_t i;

    for (i = 0; i < NUM_FILES; i++)
       mk_file(i);

    return 0;
}

在上面的代码中,当文件中的偏移量达到 2gb 时出现总线错误。这些是我尝试过的事情:

  1. 如果我将 NUM_FILES 更改为 16 并将 len 更改为 1GB,我没有任何问题。
  2. 如果我删除写入内存的 for 循环(仅 mmap),程序不会崩溃(即使 len 远大于 2gb)b/c linux 内核实际上不会将页面映射到文件,直到你读/写到映射的区域。
  3. 如果我将 mmap 调用从 MAP_SHARED 更改为 MAP_ANON(取消注释第一个 mmap 调用并注释掉第二个)并且未链接到文件,则没有问题(即使写入成功)。
  4. /dev/shm (30gb) 上有足够的空间,我这里只用了 16gb。
  5. 我不必写入每个分配的字节。我只需要写入最后一个 mmap'ed 区域(将内部 for 循环移到外面),如果偏移量 + BLOCK_SIZE >= 2gb 则出现总线错误。
  6. 我已经在 Ubuntu 13.10 和 CentOS 6.4 上尝试过,两者都有同样的问题。

我想知道这是否是linux内核中的问题?有没有人尝试过使用大于 2gb 的 MAP_SHARED 映射单个文件并成功使用(读/写)它?

4

2 回答 2

2

我相信问题在于这j是一个int. 当j命中较大的值时,会(j + 1) * BLOCK_SIZE溢出并且您的ftruncate调用不会按照您的意图进行。检查来自的返回值ftruncate应该可以确认这一点。

mmap 手册页特别指出,SIGBUS文件不支持尝试的访问。

于 2014-04-01T20:09:09.980 回答
0

我不确定这对您的情况是否有帮助。当我们遇到大文件问题时,以下帮助。我们放了宏:

#define _FILE_OFFSET_BITS 64

在包括标准标题之前。您也可以在调用 gcc 时在命令行上定义它。

于 2014-04-01T18:59:11.293 回答