1

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

4

2 回答 2

4

2 件事。

  1. 第二个参数mmap()是要在地址空间中显示的文件部分的大小。最后一个是您想要地图的文件中的偏移量。这意味着正如您所调用的那样,mmap()您只会看到文件中从偏移量 4096 开始的 1 页(在 x86 和 ARM 上为 4096 字节)。如果您的文件小于 4096 字节,则将没有映射并mmap()返回MAP_FAILED(ie (caddr_t)-1)。您没有检查函数的返回值,因此以下内容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. 唯一可移植的方法是不在内存映射上使用字符串函数。

于 2015-06-28T09:44:01.443 回答
3

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

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

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

ftruncate(fp, pageSize);

如果你想写入内存(文件),你也应该使用标志 PROT_WRITE。IE

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

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

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

另外,当然,正如@Jongware 所指出的,您应该测试openfor -1 和mmapfor 的结果MAP_FAILED

于 2015-06-28T09:34:06.213 回答