【问题标题】:Can I mmap a file with length greater than the size of the file?我可以映射长度大于文件大小的文件吗?
【发布时间】:2020-03-20 07:39:35
【问题描述】:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

我不明白使用MAP_PRIVATE 标志时mmap 的工作原理。我可以将大于文件fd 大小的length 传递给mmap 吗?这样做之后,可以读写超过文件大小但在length之内的内存吗?

我正在编写一些代码来计算文件的 MD5。我决定编写仅将数据作为void*size_t len 操作的函数,而不是使用标准库流函数。以前,我使用malloc 并在使用它们之前将文件复制到一些malloc'ed 内存中,但事实证明这对于大文件来说非常慢,并且在我发现mmap 之后非常愚蠢。

我正在处理的问题是,在计算任何数据的 MD5 之前,some padding and information is appended to the data that will be hashed. 使用之前的解决方案 malloc 我只会计算需要附加多少数据,然后 realloc 并写入。现在,我预先计算需要附加多少数据并将增加的长度传递给mmap。在小文件上这工作正常,但在大文件上尝试写入超出文件大小的地址会导致分段错误。

这大概就是我想要做的事情:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/mman.h>
#include <sys/stat.h>


// The Data + length struct
struct data{
        void* s;
        size_t len;
};

//mmap on opened file descriptor into a data struct
struct data* data_ffile(int fd)
{
        struct data* ret = malloc(sizeof(struct data));

        //Get the length of the file
        struct stat desc;
        fstat(fd, &desc);
        ret->len = (size_t)desc.st_size;

        //Calculate the length after appending
        size_t new_len =  ret->len + 1;
        if((new_len % 64) > 56)
                new_len += (64 * 2) - (new_len % 64);
        else if((new_len % 64) <= 56)
                new_len += 64 - (new_len % 64);

        //Map the file with the increased length
        ret->s = mmap(NULL, new_len, PROT_READ | PROT_WRITE,
                      MAP_PRIVATE, fd, 0);

        if(ret->s == MAP_FAILED) exit(-1);

        return ret;
}

//Append a character to the mmap'ed data
void data_addchar(struct data* w, unsigned char c)
{
        ((char*)w->s)[w->len++] = c;
        return;
}

void md5_append(struct data* md)
{
        data_addchar(md, 0x80);

        while((md->len % 64) != 56){
                data_addchar(md, (char)0);
        }
}

int main(int argc, char** argv)
{
        int fd = open(argv[1], O_RDONLY);
        struct data* in = data_ffile(fd);
        close(fd);

        md5_append(in);
}

我对@9​​87654338@有基本的误解吗?

【问题讨论】:

  • @kaylum 我已编辑问题以使代码示例可编译。 (为了简洁和可读性,我省略了错误处理和内存处理)。此示例重现了我遇到的问题,该问题是 data_addchar 上的段错误,文件很大。我使用的文件是Fedora 31 netinstall ISO (650MB)

标签: c file segmentation-fault mmap


【解决方案1】:

我可以将大于文件 fd 大小的长度传递给 mmap 吗?这样做之后,是否可以读写超出文件大小但在长度范围内的内存?

这都记录在 mmap POSIX specification:

系统应始终将任何部分页面填零 目的。此外,系统永远不会写出任何修改过的 对象最后一页超出其末尾的部分。 地址范围内的引用从 pa 开始并持续 len 字节到对象末尾之后的整个页面应导致 在传递 SIGBUS 信号时。

  1. 是的,您可以mmap长度大于文件大小,并且
  2. 访问文件末尾以外的任何页面,最后(可能是部分)页面除外,将导致SIGBUS

【讨论】:

  • 如果我mmap 的长度大于文件大小并且需要额外的页面,这是否也是如此?
  • @David 对不起,我不太明白。我以为我准确地涵盖了你的问题。如果文件长度为 8190 字节,一页为 4096 字节,并且您映射 3 页,则访问字节 [0..8191](第二页超出文件末尾 2 个字节)是可以的,并且可以访问第 3 页(不包括文件的任何部分)产生SIGBUS
猜你喜欢
  • 2018-05-16
  • 1970-01-01
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2022-11-02
  • 1970-01-01
相关资源
最近更新 更多