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

copy whole of a file into memory using mmap

我想在 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 消息。 接下来,我想迭代这个复制的文件并对它做一些事情。 我怎样才能正确复制文件?

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

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

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

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 的结果。

2 件事。

  1. mmap() 的第二个参数是您希望在您的地址 space 中可见的文件部分的大小。最后一个是您想要映射的文件中的偏移量。这意味着当您调用 mmap() 时,您只会看到文件中从偏移量 4096 开始的 1 页(在 x86 和 ARM 上为 4096 字节)。如果您的文件小于 4096 字节,那么将有 映射和 mmap() 将 return MAP_FAILED (即 (caddr_t)-1) .您没有检查函数的 return 值,因此以下 printf() 取消引用非法指针 => BUS ERROR.
  2. 将内存映射与字符串函数一起使用可能很困难。如果文件不包含二进制 0。可能会发生这些函数然后尝试 访问文件的映射大小并触摸未映射的内存 => SEGFAULT。

要为文件打开内存,您必须知道文件的大小。

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);
}

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