mmap() 在重复映射和取消映射单个页面时无法分配内存

问题描述

我已经阅读了许多 SO(和其他)问题,但找不到对我有帮助的问题。我想一次 mmap 两个文件并逐字节复制它们的内容(我知道这看起来很荒谬,但这是我最小的可重复示例)。因此,我遍历每个字节,复制它,然后在文件中一页大小之后,munmap 当前页和 mmap 下一页。我应该只需要每个文件的一页(4096 字节),所以不应该有任何内存问题。

另外,如果输出文件太小,内存通过posix_fallocate分配,运行正常。我的硬盘空间不足也不是问题。

但是,一旦我要使用约 140 MB 的更大文件,我就会从正在写入的输出文件中收到 cannot allocate memory 错误。你们知道这是怎么回事吗?

#include <sys/types.h>
#include <sys/mman.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <bitset>
#include <fcntl.h>
#include <sys/stat.h>
#include <math.h>
#include <errno.h>

using namespace std;

int main()
{

    char file_input[] = "medium_big_file";
    char file_output[] = "foo_output";
    int fd_input = -1;
    int fd_output = -1;
    unsigned char *map_page_input,*map_page_output;
    struct stat stat_input,stat_output;

    if ((fd_input = open(file_input,O_RDONLY)) == -1 ||
          (fd_output = open(file_output,O_RDWR|O_CREAT,0644)) == -1) {
            cerr << "Error on open()" << endl;
            return EXIT_FAILURE;
    }

    // get file size via stat()
    stat(file_input,&stat_input);
    stat(file_output,&stat_output);
    const size_t size_input = stat_input.st_size;
    const size_t size_output = stat_output.st_size;

    const size_t pagesize = getpagesize();

    size_t page = 0;
    size_t pos = pagesize;

    if (size_output < size_input) {
      if (posix_fallocate(fd_output,size_input) != 0) {
        cerr << "file space allocation didn't work" << endl;
        return EXIT_FAILURE;
      }
    }

    while(pos + (pagesize * (page-1)) < size_input) {
      // check if input needs the next page
      if (pos == pagesize) {
        munmap(&map_page_input,pagesize);
        map_page_input = (unsigned char*)mmap(NULL,pagesize,PROT_READ,MAP_FILE|MAP_PRIVATE,fd_input,page * pagesize);
        munmap(&map_page_output,pagesize);
        map_page_output = (unsigned char*)mmap(NULL,PROT_READ|PROT_WRITE,MAP_SHARED,fd_output,page * pagesize);
        page += 1;
        pos = 0;
        if (map_page_output == MAP_FAILED) {
      cerr << "errno: " << strerror(errno) << endl;
          cerr << "mmap failed on page " << page << endl;
          return EXIT_FAILURE;
        }
      }

      memcpy(&map_page_output[pos],&map_page_input[pos],1);

      pos += 1;
    }

    munmap(&map_page_input,pagesize);
    munmap(&map_page_output,pagesize);


    close(fd_input);
    close(fd_output);
    return EXIT_SUCCESS;
}

解决方法

循环的第一次迭代尝试取消映射从未映射过的东西,并将完全未初始化的指针传递给 munmap。不是一次,而是两次。

最后,munmap 需要一个指向 mmap-ed 内存的指针,而不是一个指向 mmap-ed 内存的指针。

显示的代码无法检查来自 munmap 的返回状态。如果确实如此,它会发现对 munmap 的每次调用都失败了(希望如此,但如果第一次调用恰好传递了一个对齐的指针,那么堆栈的一大块可能最终会被取消映射,随之而来的是欢笑) ,因此显示的代码只会不断分配越来越多的页面,并且内存不足。

您必须修复这两个错误。

,

您没有检查 munmap 的退出代码。它失败。它失败是因为您不需要获取地址的地址。替换:

munmap(&map_page_input,pagesize);

munmap(map_page_input,pagesize);

因为 munmap 失败,您用完了每个进程的最大映射数。

,

munmap 将 mmap 返回的 value 作为第一个参数。在您的代码中 munpap 接收一个指向包含它的变量的指针,因此您实际上并未取消该区域的映射。只需在 munmap 调用中删除“&”即可。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...