如何保护 mmap() 分配的内存空间不被'new'调用分配?
How can a memory space allocated by mmap() protected from getting allocated by 'new' calls?
我正在使用特定地址分配空间
mmap(...,PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,...)
但是,如果我调用类似new char[large_number]
该数组的创建与先前分配的内存段重叠。所以我的问题是我应该怎么做来保护我之前尝试分配的内存段。
编辑:对于以下代码,
char* base_address = (char*)malloc(sizeof(long));
int page_size = getpagesize();
size_t length = page_size;
char* selected_address = (char*)base_address + (page_size - ((long)base_address % page_size));
char* allocated_address = (char*) mmap(selected_address, page_size * 4, PROT_WRITE | PROT_READ
, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
int* a = new int[page_size / 4];
// mprotect(allocated_address, page_size * 4, PROT_READ | PROT_WRITE);
int* b = new (allocated_address) int[page_size / 2];
int* c = new (allocated_address + page_size / 2 ) int[page_size/2];
int* d = (int*)malloc(page_size);
cout << "base_address:" << (int*)base_address << " selected_address:" << (int*)selected_address << " allocated_address:" << (int*)allocated_address << endl;
cout << "a=" << a << " end=" << &a[page_size/4 - 1] << endl;
cout << "b=" << b << " end=" << &b[page_size/2 - 1] << endl;
cout << "c=" << c << " end=" << &c[page_size/2 - 1] << endl;
cout << "d=" << d << " end=" << &d[page_size - 1] << endl;
我得到了
base_address:0x603010 selected_address:0x604000 allocated_address:0x604000
a=0x603030 end=0x60402c
b=0x604000 end=0x605ffc
c=0x604800 end=0x6067fc
d=0x604040 end=0x60803c
在 mmap 之外分配的 a 和 d 段不应该分配空间吗?
从我的系统上 mmap 的手册页 (OS X)
如果在标志中指定了MAP_FIXED,系统将尝试将映射放置在指定的地址,可能会删除该位置已存在的映射。
您正在强迫mmap()
在malloc()
交给您的地址上方的第一页上进行映射。 new
调用malloc()
来获取内存块,并且内存分配器很可能认为它拥有您尝试映射到的页面。 您不会告诉分配器它不能使用该页面,因此它会继续执行该页面。
不要指定MAP_FIXED
。只需使用它返回给您的地址,无需指定MAP_FIXED
。
mmap(...,PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED,...)
内存映射只能是MAP_SHARED
或MAP_ANONYMOUS
,但不能两者兼而有之。
但是,如果我调用类似
new char[large_number]
该数组的创建与先前分配的内存段重叠。
这是不可能的。忽略mmap
调用的返回值,调用很可能失败。
您的实现平台可能会隐式增长堆,方法是跟踪堆使用的最高页面地址,然后在需要时简单地增加它。首次访问以前未使用的页面会生成页面错误,然后 O/S 会自动增加进程的虚拟内存地址空间,从错误的地址开始。
在 Linux 上, 参见 sbrk(2) 手册页 来获取此过程的更完整描述。
所以这里发生的事情是,你明确地请求了一些 mmap() 到一个地址,正是 C 库接下来将使用的地址,用于分配的内存。C 库愉快地继续增加数据段,侵入您的 mmap-ed 空间。
这是一个简单的案例"医生,我这样挥动手臂很痛";"好了,别这样挥胳膊了。"
Linux 上的 mmap()手册页对 mmap() 的第一个参数提供了以下警告:
如果 addr 为 NULL,则内核选择要创建的地址 映射;这是创建新地图的最便携方法。
NULL 地址会导致内核将新内存段映射到一个地址,该地址远不靠近其他任何地址,也不靠近进程的数据段,并且两者不会相互干扰。
如果像你一样,你在虚拟地址空间中显式地mmap()处理一个特定的地址,那么你有责任避免遇到麻烦,并确切地知道你的进程中发生了什么,包括C库在做什么。
我还应该注意,为了计算新的 mmaped 地址,您正在计算一个超过当前分配数组末尾的指针地址。
严格来说,这是未定义的行为。在这一点上,就严格的C/C++合规性而言,所有赌注都已关闭。
您的代码是未定义的行为。您正在mmap
-ing new
或malloc
给出的地址。您不应该这样做(并且它经常会因MAP_FIXED
而失败,因为new
或malloc
返回的地址很少是页面对齐的)。
我猜你正在使用Linux。所以代码:
char* allocated_address
= (char*) mmap(NULL, page_size * 4,
PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (allocated_address == MMAP_FAILED)
{ perror("mmap"); exit(EXIT_FAILURE); };
int* b = new ((void*)allocated_address) int[page_size / 2];
int* c = new ((void*)(allocated_address + (page_size / 2)*sizeof(int)))
int[page_size/2];
请注意,数组int[page_size/2]
的字节大小为 sizeof(int)*(page_size/2)
。在我的系统(Linux/x86-64/Debian/Sid)上,sizeof(int)
是4,page_size
是4096。
仔细阅读 mmap(2)。由于 ASLR,mmap
的结果(没有 MAP_FIXED
,您几乎不应该使用它,除非再次映射以前 mmaped 的地址段)在一次运行到下一次运行之间通常是不可重现的。
顺便说一句,当你的程序作为pid 1234运行时,在单独的终端中运行cat /proc/1234/maps
(从程序内部,你甚至可以逐行顺序读取并解析/proc/self/maps
);它将显示进程1234的地址空间。Read proc(5) 和 Advanced Linux Programming
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- unique_ptr:在分配之前调用 reset 有什么效果
- 调用析构函数以释放动态分配的内存
- 在对象指针上调用 Delete 是否会递归删除其动态分配的成员
- 如何为 std::vector 分配内存,然后稍后为某些元素调用构造函数?
- C++析构函数调用两次,堆栈分配的复合对象
- 在先前调用 string::find 后添加内存分配和内存集会导致它返回 npos.为什么?
- 在之后仍需要使用源对象时调用父移动分配运算符
- 为什么调用移动分配?
- 从 C# 调用 C++ DLib 会导致错误的分配异常
- 如何为包含最多N个元素的std::multiset调用最大数量分配
- 通过外部分配的数据调用特征 GEMM
- C++ - 从移动分配运算符调用复制分配
- 为什么分配了 nullptr 的指针可以调用成员函数?
- 分配内存并在回调时调用C++的 Rust 函数崩溃
- 默认移动分配调用析构函数,复制分配不
- C++:自动向量重新分配调用复制构造函数?为什么
- 第二个字符串在多次重新分配调用后损坏
- Windows堆分配调用堆栈-奇怪的调用堆栈
- 是否有一个位集类在实例化时大小,但避免了 boost::d ynamic_bitset<> 的额外分配调用?