mmap

Posted by keming on July 19, 2021
  • 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);