解剖linux内核之内存
2011-11-09 15:05
183 查看
结束了对内核进程的剖析,接下来将要开始对内核资源(从os角度)的剖析。OS为了更好地管理资源,会对真实的资源进行虚拟化,例如针对内存有虚拟内存(虚拟线性地址空间),针对与文件系统有虚拟文件系统(VFS层);为了更好的使用该资源会首先对资源进行抽象(即定义资源的数据结构)然后基于此在定义操作(函数)。因此,我们读内核,要遵从“虚实结合,一动一静”的原则,虚与实的结合往往便是掌握内核的关键,动与静可以从两个角度出发:一是描述该资源的数据结构,二是操作该资源的函数。
我们打印的内容有zone的名字,zone的起始物理页框号,每个zone包含的page数量,以及可用page数量(内存空洞不可使用)。代码如下:
1 //scan the zones
2
3 %{
4 #include <linux/mmzone.h>
5 #include <linux/mm.h>
6
7 structpglist_data *(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
8 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
9
10 void print_each_zone(void)
11 {
12 struct zone *zone;
13 struct pglist_data *pg_data;
14
15 pg_data =first_online_pgdat_own();
16 _stp_printf("node_id:%d\tnr_zones:%d\n",pg_data->node_id,pg_data->nr_zones);
17 _stp_printf("node_start_pfn:%ld \tnode_present_pages:%ld\tnode_spanned_pages:%ld\n\n",\
18 pg_data->node_start_pfn,pg_data->node_present_pages,pg_data->node_spanned_pages);
19 for(zone=pg_data->node_zones;zone;zone = next_zone_own(zone))
20 {
21 _stp_printf("zonename:%s\n",zone->name);
22 _stp_printf("zone_start_pfn:%ld\n",zone->zone_start_pfn);
23 _stp_printf("spanned_pages:%ld\tpresent_pages:%ld\n\n",zone->spanned_pages,zone->present_pages);
24 }
25 }
26 %}
27
28 function print_each_zone_bridge()
29 %{
30 print_each_zone();
31 %}
32 probe begin
33 {
34 print_each_zone_bridge();
35 exit();
36 }
结果如下:
node_id:0 nr_zones:3
node_start_pfn:16 node_present_pages:1039631 node_spanned_pages:1179632
zone name:DMA
zone_start_pfn:16
spanned_pages:4080 present_pages:3921
zone name:DMA32
zone_start_pfn:4096
spanned_pages:1044480 present_pages:890296
zone name:Normal
zone_start_pfn:1048576
spanned_pages:131072 present_pages:129280
zone name:Movable
zone_start_pfn:0
spanned_pages:0 present_pages:0
从结果来看,系统中一共有3个zone(多了一个movablezone,但是为NULL),一共可用的page是1039631(3921+890296+129280=1023497),但是将每个zone中的可用page数量加起来并不等于在pg_data_t中标识的总的可用page数量(原因?);总的page数量是1179632(4080+1044480+131072=1179632),pg_data_t标识的总的page数量等于每个zone中的总的page数量之和。
了解了空闲页的组织形式,我们就可以打印所有的空闲页的页框号。代码如下:
1 //scan the free pages
2
3 %{
4 #include <linux/mmzone.h>
5 #include <linux/mm.h>
6 #include <linux/spinlock.h>
7
8 struct pglist_data*(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
9 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
10
11 void print_free_pages(void)
12 {
13 unsigned long flags,order,t,pfn,flag;
14 struct list_head *i;
15 struct page *page;
16 struct zone *zone;
17 struct pglist_data *pg_data;
18
19 pg_data =first_online_pgdat_own();
20 for (zone=pg_data->node_zones;zone;zone =next_zone_own(zone))
21 {
22 if(zone-pg_data->node_zones==3)
23 break;//ignorezone_movable
24 spin_lock_irqsave(&zone->lock,flags);
25 for_each_migratetype_order(order,t){
26 list_for_each(i,&(zone->free_area[order].free_list[t])){
27 page=list_entry(i,struct page,lru);
28 if(page){
29 pfn=page_to_pfn(page);
30 _stp_printf("pfn:%ld\n",pfn);
31 }
32 }
33 }
34 spin_unlock_irqrestore(&zone->lock,flags);
35 }
36 }
37
38 %}
39
40 functionprint_free_pages_bridge()
41 %{
42 print_free_pages();
43 %}
44 probe begin
45 {
46 print_free_pages_bridge();
47 exit();
48 }
部分结果如下:
pfn:128
pfn:16
pfn:32
pfn:64
pfn:256
pfn:512
pfn:3072
pfn:2048
pfn:1024
pfn:5607
pfn:908668
pfn:7429
pfn:6498
pfn:224358
pfn:6496
pfn:6302
pfn:908488
pfn:131070
pfn:131064
pfn:7430
pfn:6860
pfn:908188
pfn:908060
pfn:907932
pfn:907804
pfn:224360
pfn:5608
…
了解了内存中已用page的结构,我们下面来打印已经使用的page:
1 %{
2 #include <linux/list.h>
3 #include <linux/mm_types.h>
4 #include <linux/mmzone.h>
5 #include <linux/spinlock.h>
6 struct pglist_data*(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
7 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
8 void print_used_pages(void)
9 {
10 struct page *page;
11 struct list_head *list,*head;
12 unsigned long i,flags,pfn;
13 struct pglist_data *pg_data;
14 struct zone *zone;
15 pg_data=first_online_pgdat_own();
16 for (zone=pg_data->node_zones;zone;zone =next_zone_own(zone)){
17 _stp_printf("zonename:%s\n",zone->name);
18 if(zone-pg_data->node_zones==3)
19 break;
20 spin_lock_irqsave(&zone->lru_lock,flags);
21 for_each_lru(i){
22 head=&zone->lru[i].list;
23 if(head->prev==head){
24 _stp_printf("lru[%ld] list is NULL\n",i);
25 continue;
26 }
27 else{
28 list_for_each(list,&zone->lru[i].list){
29 page=list_entry(list,struct page,lru);
30 if(page){
31 pfn=page_to_pfn(page);
32 _stp_printf("pfn:%ld\n",pfn);
33 }
34 }
35 }
36 }
37 spin_unlock_irqrestore(&zone->lru_lock,flags);
38 }
39
40 }
41 %}
42 functionprint_used_pages_bridge()
43 %{
44 print_used_pages();
45 %}
46
47 probe begin
48 {
49 print_used_pages_bridge();
50 exit();
51 }
部分结果如下:
zone name:DMA
lru[0] list is NULL
lru[1] list is NULL
lru[2] list is NULL
lru[3] list is NULL
lru[4] list is NULL
zone name:DMA32
lru[0] list is NULL
lru[1] list is NULL
lru[2] list is NULL
lru[3] list is NULL
lru[4] list is NULL
zone name:Normal
pfn:1106993
pfn:1121250
pfn:1107433
pfn:1116837
pfn:1116614
pfn:1179188
pfn:1119556
pfn:1110427
pfn:1118352
…
从free page(每个zone中使用free_area中链接在一起的)到usedpage(每个zone中使用lru链接在一起的)涉及到的便是内存的分配,反过来便是内存的回收。感兴趣的的自己找书看吧。
1 %{
2 #include <linux/mm_types.h>
3 #include <asm/page.h>
4 #include <linux/mm.h>
5 #include <linux/rmap.h>
6 #include <linux/sched.h>
7 #include <linux/list.h>
8 #include <linux/types.h>
9 #include <linux/prio_tree.h>
10
11 struct anon_vma *(*page_lock_anon_vma_own)(struct page *)=(struct anon_vma *(*)(struct page*))0xc01fa470;
12 void (* page_unlock_anon_vma_own)(structanon_vma *)=(void (*)(struct anon_vma *))0xc01fa3f0;
13 struct vm_area_struct*(*vma_prio_tree_next_own)(struct vm_area_struct *vma,struct prio_tree_iter*iter)=\
14 (struct vm_area_struct *(*)(structvm_area_struct *vma,struct prio_tree_iter *iter))0xc01ea8f0;
15 void print_page_owner(void)
16 {
17 long pfn;
18 struct page *page;
19 struct anon_vma *av;
20 struct task_struct*page_owner;
21 struct prio_tree_iter iter;
22 struct vm_area_struct *vma;
23 struct anon_vma_chain *avc;
24 pgoff_t pgoff;
25
26 for(pfn=0;pfn<=totalram_pages;pfn++){
27 if(!pfn_valid(pfn))
28 continue;
29 page=pfn_to_page(pfn);
30 if(!page)
31 continue;
32 if(page_mapped(page)){
33 if(PageAnon(page)){//anon page
34 _stp_printf("anon page pfn:%ld\n",pfn);
35 av=page_lock_anon_vma_own(page);
36 if(!av)
37 continue;
38 list_for_each_entry(avc,&(av->head),same_anon_vma){
39 page_owner=avc->vma->vm_mm->owner;
40 _stp_printf("\tanon page owner:%d%s\n",page_owner->pid,page_owner->comm);
41 }
42 page_unlock_anon_vma_own(av);
43 }
44 elseif(page->mapping){//file page
45 _stp_printf("file page pfn:%ld\n",pfn);
46 pgoff=page->index<< (PAGE_CACHE_SHIFT-PAGE_SHIFT);
47 for(prio_tree_iter_init(&iter,&page->mapping->i_mmap,pgoff,pgoff),vma=NULL;\
48 (vma=vma_prio_tree_next_own(vma,&iter));){
49 page_owner=vma->vm_mm->owner;
50 _stp_printf("\tfile page owner:%d%s\n",page_owner->pid,page_owner->comm);
51 }
52 }
53 }
54 }
55 }
56
57 %}
58 functionprint_page_owner_bridge()
59 %{
60 print_page_owner();
61 %}
62 probe begin
63 {
64 print_page_owner_bridge();
65 exit();
66 }
部分结果:
…
anon page pfn:79967
anon page owner:1219dbus-launch
anon page owner:1219dbus-launch
anon page pfn:79968
anon page owner:1232VBoxClient
anon page pfn:79969
anon page owner:1219dbus-launch
anon page pfn:79970
anon page owner:1228dbus-daemon
anon page owner:1228dbus-daemon
anon page pfn:79971
anon page owner:1228dbus-daemon
anon page pfn:79972
anon page owner:1219dbus-launch
anon page pfn:79973
anon page owner:1219dbus-launch
file page pfn:79974
file page owner:1304nautilus
file page owner:829Xorg
file page pfn:79975
file page owner:1304nautilus
file page owner:829Xorg
…
注:该实验是在2.6.35-22-generic上做的,在2.6.38-12-server上没有成功,原因不详。
针对于内存管理,我们可以打印所有的空闲页、使用页以及使用页的使用者,但是有没有冲突呢?有的话又是什么原因造成的?自己研究一下吧。
虚实结合
http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory这个博客讲的不错,自己的感悟后面在慢慢补充吧。动静结合
内存管理的数据结构有三个,这三个是包含的意思,从高层到底层,粒度越来越细,struct pg_data_t是最上层的结构体,在UMA机上只有一个;下一层是struct zone,一个struct pg_data_t中包含很多个struct zone;最底层的是struct page。这三者的结构如下图所示,其中zone中的struct free_area free_area[MAX_ORDER]就是管理空闲page的buddy方法。遍历系统中所有zone
针对于我们的pc机,只有一个pg_data_t结构体,系统中所有的zone并不是采用链表的形式串联在一起的,而是在pa_data_t中使用数组来表示的(如上图所示),所以找到系统中那个唯一的pa_data_t结构体的地址,便可以遍历系统中的所有zonele。我们打印的内容有zone的名字,zone的起始物理页框号,每个zone包含的page数量,以及可用page数量(内存空洞不可使用)。代码如下:
1 //scan the zones
2
3 %{
4 #include <linux/mmzone.h>
5 #include <linux/mm.h>
6
7 structpglist_data *(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
8 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
9
10 void print_each_zone(void)
11 {
12 struct zone *zone;
13 struct pglist_data *pg_data;
14
15 pg_data =first_online_pgdat_own();
16 _stp_printf("node_id:%d\tnr_zones:%d\n",pg_data->node_id,pg_data->nr_zones);
17 _stp_printf("node_start_pfn:%ld \tnode_present_pages:%ld\tnode_spanned_pages:%ld\n\n",\
18 pg_data->node_start_pfn,pg_data->node_present_pages,pg_data->node_spanned_pages);
19 for(zone=pg_data->node_zones;zone;zone = next_zone_own(zone))
20 {
21 _stp_printf("zonename:%s\n",zone->name);
22 _stp_printf("zone_start_pfn:%ld\n",zone->zone_start_pfn);
23 _stp_printf("spanned_pages:%ld\tpresent_pages:%ld\n\n",zone->spanned_pages,zone->present_pages);
24 }
25 }
26 %}
27
28 function print_each_zone_bridge()
29 %{
30 print_each_zone();
31 %}
32 probe begin
33 {
34 print_each_zone_bridge();
35 exit();
36 }
结果如下:
node_id:0 nr_zones:3
node_start_pfn:16 node_present_pages:1039631 node_spanned_pages:1179632
zone name:DMA
zone_start_pfn:16
spanned_pages:4080 present_pages:3921
zone name:DMA32
zone_start_pfn:4096
spanned_pages:1044480 present_pages:890296
zone name:Normal
zone_start_pfn:1048576
spanned_pages:131072 present_pages:129280
zone name:Movable
zone_start_pfn:0
spanned_pages:0 present_pages:0
从结果来看,系统中一共有3个zone(多了一个movablezone,但是为NULL),一共可用的page是1039631(3921+890296+129280=1023497),但是将每个zone中的可用page数量加起来并不等于在pg_data_t中标识的总的可用page数量(原因?);总的page数量是1179632(4080+1044480+131072=1179632),pg_data_t标识的总的page数量等于每个zone中的总的page数量之和。
遍历zone中的空闲页
内核为了管理页碎片,再将空闲页分为5条链表,每个zone的free_area的实际结构如下:了解了空闲页的组织形式,我们就可以打印所有的空闲页的页框号。代码如下:
1 //scan the free pages
2
3 %{
4 #include <linux/mmzone.h>
5 #include <linux/mm.h>
6 #include <linux/spinlock.h>
7
8 struct pglist_data*(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
9 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
10
11 void print_free_pages(void)
12 {
13 unsigned long flags,order,t,pfn,flag;
14 struct list_head *i;
15 struct page *page;
16 struct zone *zone;
17 struct pglist_data *pg_data;
18
19 pg_data =first_online_pgdat_own();
20 for (zone=pg_data->node_zones;zone;zone =next_zone_own(zone))
21 {
22 if(zone-pg_data->node_zones==3)
23 break;//ignorezone_movable
24 spin_lock_irqsave(&zone->lock,flags);
25 for_each_migratetype_order(order,t){
26 list_for_each(i,&(zone->free_area[order].free_list[t])){
27 page=list_entry(i,struct page,lru);
28 if(page){
29 pfn=page_to_pfn(page);
30 _stp_printf("pfn:%ld\n",pfn);
31 }
32 }
33 }
34 spin_unlock_irqrestore(&zone->lock,flags);
35 }
36 }
37
38 %}
39
40 functionprint_free_pages_bridge()
41 %{
42 print_free_pages();
43 %}
44 probe begin
45 {
46 print_free_pages_bridge();
47 exit();
48 }
部分结果如下:
pfn:128
pfn:16
pfn:32
pfn:64
pfn:256
pfn:512
pfn:3072
pfn:2048
pfn:1024
pfn:5607
pfn:908668
pfn:7429
pfn:6498
pfn:224358
pfn:6496
pfn:6302
pfn:908488
pfn:131070
pfn:131064
pfn:7430
pfn:6860
pfn:908188
pfn:908060
pfn:907932
pfn:907804
pfn:224360
pfn:5608
…
遍历zone中所有使用的page
每个zone中用lru[NR_LRU_LISTS]来记录所有已经使用的page,目前的NR_LRU_LISTS是5,即内核使用5条链表来记录已经使用的page。详细的结构如下:了解了内存中已用page的结构,我们下面来打印已经使用的page:
1 %{
2 #include <linux/list.h>
3 #include <linux/mm_types.h>
4 #include <linux/mmzone.h>
5 #include <linux/spinlock.h>
6 struct pglist_data*(*first_online_pgdat_own)(void)=(struct pglist_data*(*)(void))0xffffffff81125cd0;
7 struct zone *(*next_zone_own)(structzone *)=(struct zone *(*)(struct zone *))0xffffffff81125d60;
8 void print_used_pages(void)
9 {
10 struct page *page;
11 struct list_head *list,*head;
12 unsigned long i,flags,pfn;
13 struct pglist_data *pg_data;
14 struct zone *zone;
15 pg_data=first_online_pgdat_own();
16 for (zone=pg_data->node_zones;zone;zone =next_zone_own(zone)){
17 _stp_printf("zonename:%s\n",zone->name);
18 if(zone-pg_data->node_zones==3)
19 break;
20 spin_lock_irqsave(&zone->lru_lock,flags);
21 for_each_lru(i){
22 head=&zone->lru[i].list;
23 if(head->prev==head){
24 _stp_printf("lru[%ld] list is NULL\n",i);
25 continue;
26 }
27 else{
28 list_for_each(list,&zone->lru[i].list){
29 page=list_entry(list,struct page,lru);
30 if(page){
31 pfn=page_to_pfn(page);
32 _stp_printf("pfn:%ld\n",pfn);
33 }
34 }
35 }
36 }
37 spin_unlock_irqrestore(&zone->lru_lock,flags);
38 }
39
40 }
41 %}
42 functionprint_used_pages_bridge()
43 %{
44 print_used_pages();
45 %}
46
47 probe begin
48 {
49 print_used_pages_bridge();
50 exit();
51 }
部分结果如下:
zone name:DMA
lru[0] list is NULL
lru[1] list is NULL
lru[2] list is NULL
lru[3] list is NULL
lru[4] list is NULL
zone name:DMA32
lru[0] list is NULL
lru[1] list is NULL
lru[2] list is NULL
lru[3] list is NULL
lru[4] list is NULL
zone name:Normal
pfn:1106993
pfn:1121250
pfn:1107433
pfn:1116837
pfn:1116614
pfn:1179188
pfn:1119556
pfn:1110427
pfn:1118352
…
从free page(每个zone中使用free_area中链接在一起的)到usedpage(每个zone中使用lru链接在一起的)涉及到的便是内存的分配,反过来便是内存的回收。感兴趣的的自己找书看吧。
反向映射
想知道谁在使用page吗?反向映射便可以找到page的使用者。代码如下:1 %{
2 #include <linux/mm_types.h>
3 #include <asm/page.h>
4 #include <linux/mm.h>
5 #include <linux/rmap.h>
6 #include <linux/sched.h>
7 #include <linux/list.h>
8 #include <linux/types.h>
9 #include <linux/prio_tree.h>
10
11 struct anon_vma *(*page_lock_anon_vma_own)(struct page *)=(struct anon_vma *(*)(struct page*))0xc01fa470;
12 void (* page_unlock_anon_vma_own)(structanon_vma *)=(void (*)(struct anon_vma *))0xc01fa3f0;
13 struct vm_area_struct*(*vma_prio_tree_next_own)(struct vm_area_struct *vma,struct prio_tree_iter*iter)=\
14 (struct vm_area_struct *(*)(structvm_area_struct *vma,struct prio_tree_iter *iter))0xc01ea8f0;
15 void print_page_owner(void)
16 {
17 long pfn;
18 struct page *page;
19 struct anon_vma *av;
20 struct task_struct*page_owner;
21 struct prio_tree_iter iter;
22 struct vm_area_struct *vma;
23 struct anon_vma_chain *avc;
24 pgoff_t pgoff;
25
26 for(pfn=0;pfn<=totalram_pages;pfn++){
27 if(!pfn_valid(pfn))
28 continue;
29 page=pfn_to_page(pfn);
30 if(!page)
31 continue;
32 if(page_mapped(page)){
33 if(PageAnon(page)){//anon page
34 _stp_printf("anon page pfn:%ld\n",pfn);
35 av=page_lock_anon_vma_own(page);
36 if(!av)
37 continue;
38 list_for_each_entry(avc,&(av->head),same_anon_vma){
39 page_owner=avc->vma->vm_mm->owner;
40 _stp_printf("\tanon page owner:%d%s\n",page_owner->pid,page_owner->comm);
41 }
42 page_unlock_anon_vma_own(av);
43 }
44 elseif(page->mapping){//file page
45 _stp_printf("file page pfn:%ld\n",pfn);
46 pgoff=page->index<< (PAGE_CACHE_SHIFT-PAGE_SHIFT);
47 for(prio_tree_iter_init(&iter,&page->mapping->i_mmap,pgoff,pgoff),vma=NULL;\
48 (vma=vma_prio_tree_next_own(vma,&iter));){
49 page_owner=vma->vm_mm->owner;
50 _stp_printf("\tfile page owner:%d%s\n",page_owner->pid,page_owner->comm);
51 }
52 }
53 }
54 }
55 }
56
57 %}
58 functionprint_page_owner_bridge()
59 %{
60 print_page_owner();
61 %}
62 probe begin
63 {
64 print_page_owner_bridge();
65 exit();
66 }
部分结果:
…
anon page pfn:79967
anon page owner:1219dbus-launch
anon page owner:1219dbus-launch
anon page pfn:79968
anon page owner:1232VBoxClient
anon page pfn:79969
anon page owner:1219dbus-launch
anon page pfn:79970
anon page owner:1228dbus-daemon
anon page owner:1228dbus-daemon
anon page pfn:79971
anon page owner:1228dbus-daemon
anon page pfn:79972
anon page owner:1219dbus-launch
anon page pfn:79973
anon page owner:1219dbus-launch
file page pfn:79974
file page owner:1304nautilus
file page owner:829Xorg
file page pfn:79975
file page owner:1304nautilus
file page owner:829Xorg
…
注:该实验是在2.6.35-22-generic上做的,在2.6.38-12-server上没有成功,原因不详。
针对于内存管理,我们可以打印所有的空闲页、使用页以及使用页的使用者,但是有没有冲突呢?有的话又是什么原因造成的?自己研究一下吧。
相关文章推荐
- linux_2.6内核内存缓冲与I/O调度机制:到底是BIO还是BH?答案是BIO与BH
- Linux用户空间与内核空间(理解高端内存)
- linux-3.2.36内核启动4-setup_arch中的内存初始化3(arm平台 bootmem_init源码分析)
- linux内核中预留4M以上大内存的方法
- 从 Linux 内核访问用户空间内存
- Linux用户空间与内核空间(理解高端内存)【转】
- linux-3.2.36内核启动3-setup_arch中的内存初始化2(arm平台 分析建立页表)
- 《Linux0.11内核完全注释》读后小结 --- 内存寻址
- Linux 内核stack size 修改 限制Mongodb 内存开销
- Linux内核开发之内存与I/O访问(一)
- linux 用户空间与内核空间——高端内存详解
- 深入理解Linux内核个人小结8---内存区管理
- linux内核中分配4M以上大内存的方法
- 读深入理解Linux内核 (第9章 进程地址空间, 第二部分 ---- 内存区域)
- linux 内核空间 及 高端内存详解
- Linux 内核高-低端内存设置代码跟踪(ARM构架)
- Linux用户和组管理,查看软件缓存,通过命令查看硬件信息(cpu,版本,序列号,内存,主板,内核等)
- Linux内核开机保留大块内存的方法总结
- linux0.12内核的内存组织和进程结构
- Linux内核开发之内存与I/O访问(一)