【问题标题】:Reading a file to string with mmap使用 mmap 读取文件到字符串
【发布时间】:2013-12-25 23:23:45
【问题描述】:

我正在尝试使用 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;
}

但我在访问 f[i] 时总是收到分段错误。 我做错了什么?

【问题讨论】:

  • 你不检查openmmap的返回值。如果你这样做会怎样?

标签: c file file-io segmentation-fault mmap


【解决方案1】:

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 +++

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

  • EINVAL 我们不喜欢 addrlengthoffset(例如,他们也喜欢 大,或未在页面边界上对齐)。
  • EINVAL(自 Linux 2.6.12 起)length 为 0。
  • EINVAL flags 既不包含MAP_PRIVATE 也不包含MAP_SHARED,或者
    包含这两个值。

-EINVAL 错误是由不能为 0 的标志引起的。必须选择 MAP_PRIVATEMAP_SHARED。我已经能够通过在 Linux x86-64 上使用 MAP_PRIVATE 使其工作。

所以,您只需将 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;
}

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

size 必须是系统页面大小的整数倍。到 使用getpagesize()函数获取页面大小。

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

int pagesize = getpagesize();
size = s.st_size;
size += pagesize-(size%pagesize);

【讨论】:

  • 或者即使只是一个普通的调试器也会告诉你fNULL 在崩溃线上,这意味着对mmap 的调用失败。
  • size 不需要是页面大小的倍数;我认为 OP 的问题是需要指定 MAP_PRIVATEMAP_SHARED。来自pubs.opengroup.org/onlinepubs/7908799/xsh/mmap.html:“该实现在整个页面上执行映射操作。因此,虽然参数 len 不需要满足大小或对齐约束,但该实现将在任何映射操作中包括范围 [pa, pa + len) 指定的任何部分页面。”
  • 你是对的:我误读了关于哪些内容必须与页面边界对齐的信息。这就是 mmap 的手册页所说的: EINVAL 我们不喜欢 addr、length 或 offset(例如,它们太大,或者没有在页面边界上对齐)。 EINVAL(自 Linux 2.6.12 起)长度为 0。EINVAL 标志既不包含 MAP_PRIVATE 也不包含 MAP_SHARED,或者包含这两个值。我会更新答案:)
  • offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).。我使用位操作来实现。 pa_offset = offset &amp; ~(sysconf(_SC_PAGE_SIZE) - 1);
猜你喜欢
  • 1970-01-01
  • 2014-02-21
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2020-09-04
相关资源
最近更新 更多