【问题标题】:When offset is passed descending mmap, mmap call failed当偏移量通过递减 mmap 时, mmap 调用失败
【发布时间】:2011-02-16 12:45:05
【问题描述】:

linux下:

#free -m 缓存的已用空闲共享缓冲区总数 内存:1995 1460 534 0 68 432 -/+ 缓冲区/缓存:959 1035 交换:2055 743 1311 # cat /proc/sys/vm/overcommit_memory 0 #cat /proc/sys/vm/overcommit_ratio 50

测试代码1:

#define PER_PAGE_SIZE 4096
#define MMAP(fd,offset) mmap (NULL,PER_PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,fd,offset)


int main(){
    int j = 0;
    int fd = open("dat.tmp",O_RDWR);
    for(int i = 131071 ; i >= 0; i--){
        ++j;        
        void* r = MMAP(fd,i*4096);
        if(r ==  MAP_FAILED){
            printf("%d,%m\n",j);
            break; 
        }    
    }  
    cout << "done " << j << endl;          
    sleep(5); 
}
############## 错误信息 : # ./a.out 65513,无法分配内存 完成 65513 ... #################

测试代码2:

#define PER_PAGE_SIZE 4096
#define MMAP(fd,offset) mmap (NULL,PER_PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,fd,offset)

int main(){
    int j = 0;
    int fd = open("dat.tmp",O_RDWR);
    for(int i = 0 ; i <= 131071; i++){
        ++j;        
        void* r = MMAP(fd,i*4096);
        if(r ==  MAP_FAILED){
            printf("%d,%m\n",j);
            break; 
        }    
    }  
    cout << "done " << j << endl;          
    sleep(5); 
}

这行得通,那么,为什么??????????

【问题讨论】:

  • 如果你用“
  • 对不起,代码已重新格式化。
  • 在两种情况下:循环变量从大到小变化,和从小到大变化,mmap的行为是不同的。当循环变量由大变小并且计数器“j”达到65513时,“mmap”调用失败(不能分配内存),相反它确实有效。
  • 我无法重现该问题。 uname -a 在您的系统上的输出是什么?

标签: c linux offset mmap


【解决方案1】:

这是我的猜测。我猜第二个程序只是扩展了一个描述单个映射的内部数据结构以包含一个更多页面。第一个可以做到这一点,但它必须向后扩展,我敢打赌合并映射的特殊情况代码甚至不会检查这一点。

它停在 65513 的事实非常具有启发性。将有一些映射用于共享库等,因此您可以使用的映射少于 65536 个。并且 65536 是许多内核用于数据结构的大小。

我建议查看/proc/&lt;pid&gt;/maps 并查看在程序休眠时每种情况下列出了多少张地图。为方便起见,您可能希望在打印“完成”消息时打印出getpid() 的结果。

我无法直接复制您的问题,因此我的系统似乎已正确处理了相反的情况。我系统上uname -a 的输出是这样的:

Linux a_hostname.somewhere 2.6.35.11-83.fc14.x86_64 #1 SMP Mon Feb 7 07:06:44 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

但是这个程序确实复制了你的问题:

#include <iostream>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define PER_PAGE_SIZE 4096
#define MMAP(fd,offset) mmap (NULL,PER_PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,fd,offset)

int main()
{
   using ::std::cout;
   using ::std::endl;
   int j = 0;
   int fd = open("dat.tmp",O_RDWR);
   char catcmd[] = "cat /proc/99999/maps_padding";
   for(int i = 131071 ; i >= 0; i-=2){
      ++j;
      void* r = MMAP(fd,i*4096);
      if(r ==  MAP_FAILED){
         cout << j << ", " << strerror(errno) << '\n';
         break;
      }
   }
   ::std::snprintf(catcmd, sizeof(catcmd), "cat /proc/%d/maps", getpid());
   cout.flush();
   ::std::system(catcmd);
   cout << "done " << j << endl;
   sleep(5);
}

如您所见,如果您在后退时跳过 2,问题仍然存在。而cat /proc/&lt;pid&gt;/maps 调用system 的输出表明确实存在数千张单独的地图。

如果我停止跳过 2 并简单地向后退,我最终会得到 2 张地图,一张较大,另一张则不太大。如果可以,内核会将相邻的映射合并为一个映射。

作为进一步确证您的问题正如我所描述的那样,这是nice discussion of /proc/sys/vm/max_map_count。设置该变量可让您更改地图的数量,默认设置为 65530。

【讨论】:

  • 看看这个关于映射限制的线程:thread.gmane.org/gmane.comp.lib.glibc.user/538/focus=546
  • @ydroneaud - 感谢您的链接。知道这很有趣。我想我会把它末尾的一些信息复制到我的答案中。
  • @ydroneaud - 感谢您的回答,我将按照这些方法解决我的问题。
  • @user619600 - 接受对您最有帮助的答案被认为是礼貌的,如果它看起来通常完整且有用。
猜你喜欢
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 2013-01-11
  • 2020-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
相关资源
最近更新 更多