我可以映射长度大于文件大小的文件吗?

Can I mmap a file with length greater than the size of the file?

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

我不明白 mmap 在使用 MAP_PRIVATE 标志时是如何工作的。我可以将大于文件 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);
}

我是不是对mmap有基本的误解?

Can I pass a length greater than the size of file fd to mmap? After doing so, can I write and read the memory that exceeds the size of the file but is within length?

这些都记录在 mmap POSIX specification:

The system shall always zero-fill any partial page at the end of an object. Further, the system shall never write out any modified portions of the last page of an object which are beyond its end. References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object shall result in delivery of a SIGBUS signal.

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