当偏移量通过mmap递减时,mmap调用失败

时间:2011-02-16 12:45:05

标签: c linux offset mmap

在linux下:

#free -m
             total       used       free     shared    buffers     cached
Mem:          1995       1460        534          0         68        432
-/+ buffers/cache:        959       1035
Swap:         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); 
}
##############
error  message :
# ./a.out 
65513,Cannot allocate memory
done 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); 
}

这是有效的,所以,为什么???????????

1 个答案:

答案 0 :(得分:4)

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

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

我建议查看/proc/<pid>/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/<pid>/mapssystem的输出显示确实存在数千张单独的地图。

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

作为进一步确凿的证据证明你的问题与我所描述的一样,就是nice discussion of /proc/sys/vm/max_map_count。设置该变量可以更改有多少个地图,默认设置为65530。