日期:2014-05-16  浏览次数:20715 次

Linux mmap调用使用经验
Linux的mmap系统调用(libc封装了同名函数)可以分配一段匿名的虚拟内存区域,也可以映射一个文件到内存。下面讨论匿名映射。

写一个简单的测试程序(见附件),启动时显示程序的虚拟内存和物理内存:
VmSize:     4300 kB
VmRSS:       356 kB


分配虚拟内存区域
下面代码是分配10MB的虚拟内存区域:
int bufferlen = 10 << 20;
char *buffer = (char*) mmap(NULL, bufferlen, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
此时增加了10MB虚拟内存,但物理内存基本没变:
VmSize:    14544 kB
VmRSS:       360 kB

分配物理内存页面

mmap仅仅是分配了虚拟内存区域,而没有马上分配物理内存。在初次访问一个虚拟页面时,操作系统才会为其分配一个物理页面。下面代码是往每个虚拟页面写入1个字节,从而导致物理内存分配:

int pagesize = sysconf(_SC_PAGESIZE);
for (i = 0; i < bufferlen; i += pagesize)
    buffer[i] = 1;

此时增加了10MB的物理内存:
VmSize:    14548 kB
VmRSS:     10640 kB


解除内存映射

使用munmap可以解除内存映射从而释放内存,刚才我们分配了10MB虚拟内存,可以全部释放,也可以释放一部分,如下面的代码是释放其中5MB内存:

munmap(buffer, bufferlen / 2);

此时虚拟内存和物理内存都减少了5M:

VmSize:     9432 kB
VmRSS:      5652 kB

原10MB区域会被分割两个5MB区域,前面一个释放,后面一个保留。

固定映射
可以通过参数指定映射的虚拟内存区域:

buffer = (char*) mmap(buffer, bufferlen, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

前两个参数指定虚拟内存区域[buffer, buffer + bufferlen),但这个区域在前面已经分配了,而且没有完全释放(只释放了一半),怎么处理这种情况呢?

如果第4个参数没有MAP_FIXED标志,则mmap自动选择其他可用区域(返回的地址不等于指定的地址);
如果使用了MAP_FIXED标志,则会强制使用这段内存,如果已经占用了就先释放、再重新创建。
可以看到强制占用这段内存区域之后,原有的内存被释放了:
VmSize:    14556 kB
VmRSS:       532 kB

因为是先释放原有的5MB虚拟内存和物理内存,再创建10MB虚拟内存,但没有分配物理内存。所以增加了5MB虚拟内存,但是减少了5MB物理内存。