使用mmap将整个文件复制到内存中

时间:2015-06-28 09:10:49

标签: c file-io mmap bus-error

我想在C.i中使用mmap将整个文件复制到内存中编写此代码:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
int main(int arg, char *argv[])
{
    char c ;
    int numOfWs = 0 ;
    int numOfPr = 0 ;
    int numberOfCharacters ;
    int i=0;
    int k;
    int pageSize = getpagesize();
    char *data;
    float wsP = 0;
    float prP = 0;
    int fp = open("2.txt", O_RDWR);
    data = mmap((caddr_t)0, pageSize, PROT_READ, MAP_SHARED, fp,pageSize);
    printf("%s\n", data);
    exit(0);
}

当我执行代码时,我收到Bus error消息。 接下来,我想迭代这个复制的文件并做一些事情。 我该如何正确复制文件?

2 个答案:

答案 0 :(得分:4)

2件事。

  1. mmap()的第二个参数是您希望在地址空间中显示的文件部分的大小。最后一个是您想要地图的文件中的偏移量。这意味着,当您调用mmap()时,您将只看到1页(在x86和ARM上它是4096字节),从文件中的偏移量4096开始。如果您的文件小于4096字节,则会有映射,而mmap()将返回MAP_FAILED(即(caddr_t)-1)。您没有检查函数的返回值,因此以下printf()取消引用非法指针 =&gt; BUS ERROR
  2. 使用带字符串函数的内存映射可能很困难。如果文件不包含二进制0.可能会发生这些函数然后尝试访问文件的映射大小并触摸未映射的内存 =&gt;段错误。
  3. 要打开文件的内存,您必须知道文件的大小。

    struct stat filestat;
    
    if(fstat(fd, &filestat) !=0) {
       perror("stat failed");
       exit(1);
    }
    
    data = mmap(NULL, filestat.st_size, PROT_READ, MAP_SHARED, fp, 0);
    if(data == MAP_FAILED) {
       perror("mmap failed");
       exit(2);
    }
    

    编辑:将始终打开内存映射,其大小为pagesize的倍数。这意味着最后一页将填充0直到下一个pagesize的倍数。通常,使用具有字符串函数的内存映射文件的程序(如printf())在大多数情况下都可以正常工作,但在映射文件大小恰好是页面大小的倍数(4096,8192,12288等)时会突然崩溃)。传递给mmap()大小超过实际文件大小的常见建议适用于Linux,但不可移植,甚至违反了Posix,后者明确指出超出文件大小的映射为undefined behaviour。唯一可移植的方法是在内存映射上使用字符串函数。

答案 1 :(得分:3)

mmap的最后一个参数是文件中的偏移量,其中映射到内存的文件部分开始。在你的情况下它应为0

data = mmap(NULL, pageSize, PROT_READ, MAP_SHARED, fp,0);

如果您的文件短于pageSize,您将无法使用超出文件末尾的地址。要使用完整尺寸,您应在调用pageSize之前将尺寸扩大到mmap。使用类似的东西:

ftruncate(fp, pageSize);

如果要写入内存(文件),也应使用标志PROT_WRITE。即。

    data = mmap(NULL, pageSize, PROT_READ|PROT_WRITE, MAP_SHARED, fp,0);

如果您的文件不包含0个字符(作为字符串的结尾)并且您想将其打印为字符串,则应使用明确指定的最大大小的printf

 printf("%.*s\n", pageSize, data);

当然,正如@Jongware指出的那样,您应该测试open的结果为-1,mmapMAP_FAILED的结果。