您的位置:首页 > 运维架构 > Linux

解剖linux内核之内存

2011-11-09 15:05 183 查看
结束了对内核进程的剖析,接下来将要开始对内核资源(从os角度)的剖析。OS为了更好地管理资源,会对真实的资源进行虚拟化,例如针对内存有虚拟内存(虚拟线性地址空间),针对与文件系统有虚拟文件系统(VFS层);为了更好的使用该资源会首先对资源进行抽象(即定义资源的数据结构)然后基于此在定义操作(函数)。因此,我们读内核,要遵从“虚实结合,一动一静”的原则,虚与实的结合往往便是掌握内核的关键,动与静可以从两个角度出发:一是描述该资源的数据结构,二是操作该资源的函数。

虚实结合

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上没有成功,原因不详。

针对于内存管理,我们可以打印所有的空闲页、使用页以及使用页的使用者,但是有没有冲突呢?有的话又是什么原因造成的?自己研究一下吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: