使用mmap将文件读取到字符串

arn*_*app 5 c file-io mmap file segmentation-fault

我正在尝试使用mmap将文件读取到字符串.

我跟随这个例子:http://www.lemoda.net/c/mmap-example/index.html

我的代码看起来像这样

unsigned char *f;
int size;
int main(int argc, char const *argv[])
{
    struct stat s;
    const char * file_name = argv[1];
    int fd = open (argv[1], O_RDONLY);

    /* Get the size of the file. */
    int status = fstat (fd, & s);
    size = s.st_size;

    f = (char *) mmap (0, size, PROT_READ, 0, fd, 0);
    for (i = 0; i < size; i++) {
        char c;

        c = f[i];
        putchar(c);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是在访问f [i]时我总是收到一个segemation错误.我究竟做错了什么?

mcl*_*fix 11

strace 你的朋友在这里:

$ strace ./mmap-example mmap-example.c

...
... (lots of output)
...
open("mmap-example.c", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=582, ...}) = 0
mmap(NULL, 582, PROT_READ, MAP_FILE, 3, 0) = -1 EINVAL (Invalid argument)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Run Code Online (Sandbox Code Playgroud)

mmap手册页会告诉你所有你需要知道的;)

  • EINVAL我们不喜欢addr,, lengthoffset(例如,它们太大,或者没有在页面边界上对齐).
  • EINVAL(自Linux 2.6.12起)length为0.
  • EINVAL flags既不含有MAP_PRIVATEMAP_SHARED,或
    包含这两个值.

-EINVAL错误是由标记,它们不能为0要么引起MAP_PRIVATEMAP_SHARED已被拾取.我已经能够MAP_PRIVATE在Linux,x86-64上使用它.

因此,您只需将MAP_PRIVATE添加到mmap():

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>

int main(int argc, char const *argv[])
{
    unsigned char *f;
    int size;
    struct stat s;
    const char * file_name = argv[1];
    int fd = open (argv[1], O_RDONLY);

    /* Get the size of the file. */
    int status = fstat (fd, & s);
    size = s.st_size;

    f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0);
    for (int i = 0; i < size; i++) {
        char c;

        c = f[i];
        putchar(c);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

注意:我的第一个答案包括另一个可能的原因EINVAL:

size必须是系统页面大小的整数倍.要获取页面大小,请使用该功能getpagesize().

这实际上并不是必需的,但您必须考虑到这两种方式,映射将始终以系统页面大小的倍数执行,因此如果您想通过返回的指针计算实际可用的内存量,请更新size如下:

int pagesize = getpagesize();
size = s.st_size;
size += pagesize-(size%pagesize);
Run Code Online (Sandbox Code Playgroud)