-
mmap默认会有
250k
左右的全局开销。 -
可以对重叠地址重复mmap,至少这组参数可以:
void *p = mmap((void *)allocate_start_addr, kSpace64M, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
- 第二次mmap会解除第一次map虚拟地址与物理地址的联系,并释放物理内存!
-
unmap非常智能,只要start_addr合法,长度合法,就永远不会返回-1,可以unmap一部分mmap地址,可以unmap多个mmap的区域合集,可以unmap那些没有被mmap过的地方,
地址对齐,随便unmap,效果就是你想要的。
-
最佳实践:使用
PROT_NONE
预留虚拟地址,然后再mmap一部分PROT_READ | PROT_WRITE
出来用。- go语言是这么做的,且不会用unmap归还虚拟地址。似乎是写了汇编解除虚拟内存与物理内存联系(madvice)并释放物理内存,虚拟地址保留住(早期版本是512G)。
- 这种方式可以防止一些
奇怪的预分配
,我发生过mmap128M,什么都不干就占2M内存的事情,可能与某些系统配置有关。- https://blog.csdn.net/weixin_35664258/article/details/83348133
- go语言runtime牛逼!
-
mmap的限制
-
不同访问权限的地址段,不能超过65530(奇怪的数字), sysctl vm.max_map_count
-
这个数字是可以更改的: https://stackoverflow.com/questions/42889241/how-to-increase-vm-max-map-count
-
int main() { uint64_t allocate_start_addr = 0x0000200000000000ul; void *p = nullptr; cout << "test for segments limit for double mmap-----------------------" << endl; int chunks_num = 10; for (int i = 0; i < chunks_num; i++) { // 128 * 4 M p = mmap((void *)(allocate_start_addr + i * kSpace128M), kSpace128M, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); } for (uint64_t i = 0; i < kSpace128M / kSpace4K * chunks_num; i++) { if ((i & 1) == 0) { continue; } uint64_t start_addr = allocate_start_addr + i * kSpace4K; try { p = mmap((void *)(start_addr), kSpace4K, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); } catch (const std::exception &e) { std::cerr << "segments : " << i << endl; std::cerr << e.what() << '\n'; } if (p == nullptr || p == MAP_FAILED) { std::cerr << "segments : " << i << endl; std::cerr << "error: " << errno << endl; break; } // try touch int *tmp = (int *)p; tmp[10] = 9; } return 0; }
-
突破mmap段数限制的一种方法
- reserve+mprotect
// reserve
void *addr = mmap(reinterpret_cast<void *>(hint), size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
// mprotect
int ret = mprotect(reinterpret_cast<void *>(hint), size, PROT_READ | PROT_WRITE);