C语言 使用 mmap 读取文件到字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20460670/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 10:28:57  来源:igfitidea点击:

Reading a file to string with mmap

cfilefile-iosegmentation-faultmmap

提问by arnoapp

I'm trying to read a file to a string using mmap.

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

I was following this example: http://www.lemoda.net/c/mmap-example/index.html

我正在关注这个例子:http: //www.lemoda.net/c/mmap-example/index.html

My code looks like this

我的代码看起来像这样

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

But I always receive a segemation fault when accessing f[i]. What am I doing wrong?

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

回答by mcleod_ideafix

straceis your friend here:

strace你的朋友在这里吗:

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

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

The mmapman page tells you all you need to know ;)

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

  • EINVALWe don't like addr, length, or offset(e.g., they are too large, or not aligned on a page boundary).
  • EINVAL(since Linux 2.6.12) lengthwas 0.
  • EINVALflagscontained neither MAP_PRIVATEor MAP_SHARED, or
    contained both of these values.
  • EINVAL我们不喜欢addr, length, 或offset(例如,它们太大,或未在页面边界上对齐)。
  • EINVAL(自 Linux 2.6.12 起)length为 0。
  • EINVALflags不包含MAP_PRIVATEMAP_SHARED,或
    包含这两个值。

The -EINVALerror is caused by flags that cannot be 0. Either MAP_PRIVATEor MAP_SHAREDhas to be picked. I have been able to make it work by using MAP_PRIVATEon Linux, x86-64.

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

So, you have just to add MAP_PRIVATE to mmap():

因此,您只需将 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;
}


NOTE: My first answer did include another possible cause for the EINVAL:

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

sizemust be an integral multiple of the page size of the system. To obtain the page size use the function getpagesize().

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

This is not actually required, but you must take into account that either way, mapping will be always performed in multiples of the system page size, so if you'd like to calculate how much memory is actually been available through the returned pointer, update sizeas this:

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

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