重复映射和取消映射单个页面时,mmap() 无法分配内存
mmap() cannot allocate memory when repeatedly mapping and unmapping one single page
我已经阅读了很多 SO(和其他)问题,但找不到对我有帮助的问题。我想一次 mmap
两个文件并逐字节复制它们的内容(我知道这看起来很荒谬,但这是我最小的可重现示例)。因此,我遍历每个字节,复制它,在文件中的一页大小之后,我 munmap
当前页面和 mmap
下一页。 Imo 每个文件应该只需要一页(4096 字节),所以不应该有任何内存问题。
此外,如果输出文件太小,内存通过posix_fallocate
分配,运行良好。我的硬盘内存不足 space 在我看来也不是问题。
但是,当我要使用大约 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, 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;
}
循环的第一次迭代尝试取消映射从未映射的内容,并将完全未初始化的指针传递给 munmap
。不是一次,是两次。
最后,munmap
需要一个指向映射内存的指针,而不是指向映射内存的指针。
显示的代码无法检查 munmap
的 return 状态。如果是这样,它会发现对 munmap
的每次调用都会失败(希望如此,但如果第一次调用恰好传递了一个对齐的指针,那么堆栈的一部分可能最终会被取消映射,随之而来的是欢闹) ,所以显示的代码只是不断分配越来越多的页面,并且 运行 内存不足。
您必须修复这两个错误。
您没有检查 munmap
的退出代码。它失败。它失败了,因为您不需要获取地址的地址。替换:
munmap(&map_page_input, pagesize);
与
munmap(map_page_input, pagesize);
因为 munmap 失败,您 运行 超出了每个进程的最大映射数。
munmap 将 mmap 返回的 value 作为第一个参数。在您的代码中, munpap 接收到一个指向包含它的变量的指针,因此您实际上并没有取消映射该区域。只需在 munmap 调用中删除“&”。
我已经阅读了很多 SO(和其他)问题,但找不到对我有帮助的问题。我想一次 mmap
两个文件并逐字节复制它们的内容(我知道这看起来很荒谬,但这是我最小的可重现示例)。因此,我遍历每个字节,复制它,在文件中的一页大小之后,我 munmap
当前页面和 mmap
下一页。 Imo 每个文件应该只需要一页(4096 字节),所以不应该有任何内存问题。
此外,如果输出文件太小,内存通过posix_fallocate
分配,运行良好。我的硬盘内存不足 space 在我看来也不是问题。
但是,当我要使用大约 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, 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;
}
循环的第一次迭代尝试取消映射从未映射的内容,并将完全未初始化的指针传递给 munmap
。不是一次,是两次。
最后,munmap
需要一个指向映射内存的指针,而不是指向映射内存的指针。
显示的代码无法检查 munmap
的 return 状态。如果是这样,它会发现对 munmap
的每次调用都会失败(希望如此,但如果第一次调用恰好传递了一个对齐的指针,那么堆栈的一部分可能最终会被取消映射,随之而来的是欢闹) ,所以显示的代码只是不断分配越来越多的页面,并且 运行 内存不足。
您必须修复这两个错误。
您没有检查 munmap
的退出代码。它失败。它失败了,因为您不需要获取地址的地址。替换:
munmap(&map_page_input, pagesize);
与
munmap(map_page_input, pagesize);
因为 munmap 失败,您 运行 超出了每个进程的最大映射数。
munmap 将 mmap 返回的 value 作为第一个参数。在您的代码中, munpap 接收到一个指向包含它的变量的指针,因此您实际上并没有取消映射该区域。只需在 munmap 调用中删除“&”。