问题描述
我已经阅读了许多 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 调用中删除“&”即可。