Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
230 views
in Technique[技术] by (71.8m points)

c++ - mmap() cannot allocate memory when repeatedly mapping and unmapping one single page

I have read many SO (and other) questions, but I couldn't find one that helped me. I want to mmap two files at once and copy their content byte-by-byte (I know this seems ridiculous, but this is my minimal reproducibly example). Therefore I loop through every byte, copy it, and after the size of one page in my files, I munmap the current page and mmap the next page. Imo there should only ever be one page (4096 bytes) of each file be needed so there shouldn't be any memory problem.

Also, if the output file is too small, the memory is allocated via posix_fallocate, which runs fine. I a lack of memory space in the hard drive can't be the problem either imo.

But as soon as I am going for a bit larger files with ~140 MB, I get the cannot allocate memory error from the output-file that I am writing into. Do you guys have any idea how this is?

#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, 0, 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, pagesize,
          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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The very first iteration of the loop attempts to unmap something that was never mapped, and passes a completely uninitialized pointer to munmap. Not once, but twice.

Finally, munmap expects a pointer to the mmap-ed memory, and not a pointer to a pointer to the mmap-ed memory.

The shown code fails to check the return status from munmap. If it did, it would've discovered that every call to munmap fails (hopefully, but if the first call happens to pass an aligned pointer, a chunk of the stack might end up being unmapped, with the ensuing hilarity), so the shown code just keeps allocating more, and more pages, and running out of memory.

You must fix both bugs.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...