我们先来看幅图:
Linux内存管理的最底层是buddy内存管理方案,即伙伴算法,管理伙伴算法我们不做详诉,有兴趣的可以自行学习,我们这里只要知道buddy内存池中只能分配2^n个page的内存,比如1,2,4,8……个pages,然而正常使用的时候不会碰巧就需要1,2,4,8……个pages。所以基于buddy内存池,还需要有上一级的内存管理系统,内核里面采用的是slab,slub,slob;而用户空间也有自己的内存管理方案,比如基于glibc的malloc/free内存管理方案。
关于glibc的malloc内存管理方案本文先不做详诉,我们大概了解以下几点,以便对本文的话题有个更加宏观的认识:
如果glibc的内存池中没有内存了,那么malloc就会通过mmap或者brk向buddy申请内存。
free调用不会立即将内存还给buddy,而是会还给glibc的内存池。
如果glibc的内存池中还有足够的内存,那么malloc直接从glibc内存池中分配内存,不会像buddy申请内存。
关于mmap的行为总结下来就是一下几点:
- mmap的时候只会创建一个vma,此vma也有可能跟之前的vma合并成一个vma,并不会真正的分配物理内存。
- 当读mmap出来的内存的时候,会发生缺页异常,并将读所涉及到内存都映射到同一个物理页,称为zero page,zero page只有读权限,没有写权限。
- 当写mmap出来的内存的时候,会在此发生缺页异常,并将所涉及到的内存映射到真实的物理页,并且这些物理页有写权限。
本文的目的就是从行为以及代码两个方面来印证以上描述。
mmap代码验证如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#define MMAP_SIZE 0x4000
int main(char *argv[], int argc)
{
int i;
int tmp;
void *addr;
char *p;
pid_t process_id;
process_id = getpid();
printf("pid is %d\n", process_id);
printf("before mmap\n");
getchar();
addr = mmap(NULL, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
printf("after mmap\n");
getchar();
p = (char *)addr;
for(i=0; i< MMAP_SIZE;i++)
tmp = p[i];
printf("after read\n");
getchar();
for(i=0; i<MMAP_SIZE; i++)
p[i] = 0x1;
printf("after write\n");
getchar();
return 0;
}
mmap之前的page fault中断以及内存分配情况
执行test程序,打印出before mmap后,通过pidof 找到测试程序的pid为13070,并获取获得如下关键信息:
virtual-machine:~/work$ ps -o maj_flt -o min_flt -p 13070
MAJFL MINFL
0 77
virtual-machine:~/work$ pmap 13070
13070: ./test
0000000000400000 4K r-x-- test
0000000000600000 4K r---- test
0000000000601000 4K rw--- test
0000000001d10000 132K rw--- [ anon ]
00007f2f56000000 1792K r-x-- libc-2.23.so
00007f2f561c0000 2048K ----- libc-2.23.so
00007f2f563c0000 16K r---- libc-2.23.so
00007f2f563c4000 8K rw--- libc-2.23.so
00007f2f563c6000 16K rw--- [ anon ]
00007f2f563ca000 152K r-x-- ld-2.23.so
00007f2f565d0000 12K rw--- [ anon ]
00007f2f565ef000 4K r---- ld-2.23.so
00007f2f565f0000 4K rw--- ld-2.23.so
00007f2f565f1000 4K rw--- [ anon ]
00007ffc66eab000 132K rw--- [ stack ]
00007ffc66f66000 12K r---- [ anon ]
00007ffc66f69000 8K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 4356K
缺页异常数量是77,所有的vma内存分配如上。前的十六进制数字表示分配该段虚拟内存的起始地址。
after mmap之后搜集信息如下:
virtual-machine:~/work$ ps -o maj_flt -o min_flt -p 13070
MAJFL MINFL
0 77
virtual-machine:~/work$ pmap 13070
13070: ./test
0000000000400000 4K r-x-- test
0000000000600000 4K r---- test
0000000000601000 4K rw--- test
0000000001d10000 132K rw--- [ anon ]
00007f2f56000000 1792K r-x-- libc-2.23.so
00007f2f561c0000 2048K ----- libc-2.23.so
00007f2f563c0000 16K r---- libc-2.23.so
00007f2f563c4000 8K rw--- libc-2.23.so
00007f2f563c6000 16K rw--- [ anon ]
00007f2f563ca000 152K r-x-- ld-2.23.so
00007f2f565d0000 12K rw--- [ anon ]
00007f2f565eb000 16K rw--- [ anon ]
00007f2f565ef000 4K r---- ld-2.23.so
00007f2f565f0000 4K rw--- ld-2.23.so
00007f2f565f1000 4K rw--- [ anon ]
00007ffc66eab000 132K rw--- [ stack ]
00007ffc66f66000 12K r---- [ anon ]
00007ffc66f69000 8K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 4372K
我们看到page fault没有增加,但是vma增加了一个:
00007f2f565eb000 16K rw--- [ anon ]
那么哪里可以找到实际分配的物理内存的大小呢?可以通过
pmap -x查看,RSS这里是0;方法二,也可以通过可以通过查看/proc/13070/smaps文件来确认
Address Kbytes RSS Dirty Mode Mapping
...
00007f2f565eb000 16 0 0 rw--- [ anon ]
...
---------------- ------- ------- -------
total kB 4372 1328 80
after read之后信息搜集如下:
virtual-machine:~/work$ ps -o maj_flt -o min_flt -p 13070
MAJFL MINFL
0 81
wqq@wqq-virtual-machine:~/work$ pmap 13070
13070: ./test
0000000000400000 4K r-x-- test
0000000000600000 4K r---- test
0000000000601000 4K rw--- test
0000000001d10000 132K rw--- [ anon ]
00007f2f56000000 1792K r-x-- libc-2.23.so
00007f2f561c0000 2048K ----- libc-2.23.so
00007f2f563c0000 16K r---- libc-2.23.so
00007f2f563c4000 8K rw--- libc-2.23.so
00007f2f563c6000 16K rw--- [ anon ]
00007f2f563ca000 152K r-x-- ld-2.23.so
00007f2f565d0000 12K rw--- [ anon ]
00007f2f565eb000 16K rw--- [ anon ]
00007f2f565ef000 4K r---- ld-2.23.so
00007f2f565f0000 4K rw--- ld-2.23.so
00007f2f565f1000 4K rw--- [ anon ]
00007ffc66eab000 132K rw--- [ stack ]
00007ffc66f66000 12K r---- [ anon ]
00007ffc66f69000 8K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 4372K
缺页异常增加了四个,因为有4个page被读了嘛!然而Rss没有增加,说明根本没有分配实际的物理内存,完全符合预期。
after write之后信息搜集如下:
virtual-machine:~/work$ ps -o maj_flt -o min_flt -p 13070
MAJFL MINFL
0 85
virtual-machine:~/work$ pmap -x 13070
13070: ./test
Address Kbytes RSS Dirty Mode Mapping
...
00007f2f565eb000 16 16 16 rw--- [ anon ]
...
---------------- ------- ------- -------
total kB 4372 1344 96
新增了四个page fault,并且RSS是16K,此时才真正的分配了物理内存。
通过查看/proc/13070/smaps也发现如下:
7f2f565eb000-7f2f565ef000 rw-p 00000000 00:00 0
Size: 16 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 16 kB
Pss: 16 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 16 kB
Referenced: 16 kB
Anonymous: 16 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
ProtectionKey: 0