Chinaunix

标题: 一个真实的FreeBSD进程的地址空间的探察分析 [打印本页]

作者: 雨丝风片    时间: 2006-05-09 15:42
标题: 一个真实的FreeBSD进程的地址空间的探察分析
一个真实的FreeBSD进程的地址空间的探察分析

雨丝风片:chinaunix.net


为了形象地描述一个真实的FreeBSD进程的地址空间的组织结构,我写了一个简单的程序,然后使用FreeBSD的kvm接口探察组成它的虚拟地址空间的数据结构,希望通过形象的图示和对真实数据的分析来讲述相关的知识,以便和代码阅读、分析形成有效互补。内核空间原是一篇漆黑,一切都只能停留在由内核源代码产生的想象中。现在有了kvm这个手电筒,就可以借着这点亮光探索前进了。希望大家能够多提意见和问题,多指出方向,以便我们能探索出更大的未知领域。

参考文献:
【FreeBSD操作系统设计与实现】第五章:Memory Management
【FreeBSD虚存系统splay树的基本原理】
【FreeBSD虚存系统splay树的代码分析】
【使用kvm接口探察内核空间的方法】

作为测试进程的小程序很简单:

  1.       1    int main(void)
  2.       2    {
  3.       3        for(;;)
  4.       4            ;
  5.       5    }
复制代码


一旦运行,这个进程便会陷入无限循环中,处于一种相对静止的状态,方便我们使用kvm接口对其进行探察。我们从描述其地址空间的vmspace结构体开始,把vm_map结构体和描述其地址空间中的连续区域的所有vm_map_entry结构体的内容都打印了出来,如下图1所示:


图1 一个真实进程的地址空间的组成结构图示


图1中蓝色小方块就是vm_map_entry结构体,除了位于vm_map结构体中的一个是用来作为头节点之外,实际用来描述测试进程地址空间的vm_map_entry结构体共有11个,它们分别表示地址空间中的一段连续区域。这11个vm_map_entry结构体是按照splay树的形态绘制的,树中的左右孩子指针在图1中用黑色箭头表示。我们知道,vm_map_entry结构体除了组织成splay树之外,还组织成了一个双向循环链表,其中,prev指针在图1中用红色箭头表示,而next指针则用蓝色箭头表示。注意,vm_map结构体里面的header也是这个双向循环链表中的一员。

图1中下方的方块分别给出了每个vm_map_entry结构体所表示的内存区域的大小,地址相邻的vm_map_entry结构体画在一排,我们可以看出,地址空间实际上由三大块组成。

下面我们来看看所打印出来的每个数字的实际含义。

首先来看看vmspace结构体:

  1.     space.vm_swrss = 0
  2.     space.vm_tsize = 1
  3.     space.vm_dsize = 1
  4.     space.vm_ssize = 32
  5.     space.vm_taddr = 0x8048000
  6.     space.vm_daddr = 0x8049000
  7.     space.vm_maxsaddr = 0xbbc00000
  8.     space.vm_exitingcnt = 0
  9.     space.vm_refcnt = 1
复制代码


我们再来看看vmspace结构体中的vm_map结构体的内容:

  1.     vm_map.nentries = 11
  2.     vm_map.size = 1196032
  3.     vm_map.timestamp = 0x28
  4.     vm_map.needs_wakeup = 0
  5.     vm_map.system_map = 0
  6.     vm_map.flags = 0x0
  7.     vm_map.root = 0xc209a660
  8.     vm_map.pmap = 0xc1de9da4
复制代码


下面我们就来看看图1中的那些vm_map_entry结构体的内容:

  1. header.prev = 0xc209a61c
  2. header.next = 0xc209a5d8
  3. header.left = 0x0
  4. header.right = 0x0
  5. header.start = 0x0
  6. header.end = 0xbfc00000
  7. header.avail_ssize = 0
  8. header.adj_free = 0
  9. header.max_free = 0
  10. header.offset = 0
  11. header.eflags = 0x0

  12. sn = 1
  13. entry addr = 0xc209a5d8
  14. vm_map_entry.prev = 0xc1de9ce4
  15. vm_map_entry.next = 0xc209a594
  16. vm_map_entry.left = 0x0
  17. vm_map_entry.right = 0x0
  18. vm_map_entry.start = 0x8048000
  19. vm_map_entry.end = 0x8049000
  20. vm_map_entry.avail_ssize = 0x0
  21. vm_map_entry.adj_free = 0
  22. vm_map_entry.max_free = 0
  23. vm_map_entry.offset = 0
  24. vm_map_entry.eflags = 0x40c


  25. sn = 2
  26. entry addr = 0xc209a594
  27. vm_map_entry.prev = 0xc209a5d8
  28. vm_map_entry.next = 0xc2057220
  29. vm_map_entry.left = 0xc209a5d8
  30. vm_map_entry.right = 0x0
  31. vm_map_entry.start = 0x8049000
  32. vm_map_entry.end = 0x804a000
  33. vm_map_entry.avail_ssize = 0x0
  34. vm_map_entry.adj_free = 536866816
  35. vm_map_entry.max_free = 536866816
  36. vm_map_entry.offset = 0
  37. vm_map_entry.eflags = 0x0


  38. sn = 3
  39. entry addr = 0xc2057220
  40. vm_map_entry.prev = 0xc209a594
  41. vm_map_entry.next = 0xc209a484
  42. vm_map_entry.left = 0xc209a594
  43. vm_map_entry.right = 0xc209a484
  44. vm_map_entry.start = 0x28049000
  45. vm_map_entry.end = 0x28066000
  46. vm_map_entry.avail_ssize = 0x0
  47. vm_map_entry.adj_free = 0
  48. vm_map_entry.max_free = 536866816
  49. vm_map_entry.offset = 0
  50. vm_map_entry.eflags = 0x40c


  51. sn = 4
  52. entry addr = 0xc209a484
  53. vm_map_entry.prev = 0xc2057220
  54. vm_map_entry.next = 0xc1fd0e58
  55. vm_map_entry.left = 0x0
  56. vm_map_entry.right = 0x0
  57. vm_map_entry.start = 0x28066000
  58. vm_map_entry.end = 0x28067000
  59. vm_map_entry.avail_ssize = 0x0
  60. vm_map_entry.adj_free = 0
  61. vm_map_entry.max_free = 0
  62. vm_map_entry.offset = 0
  63. vm_map_entry.eflags = 0x4


  64. sn = 5
  65. entry addr = 0xc1fd0e58
  66. vm_map_entry.prev = 0xc209a484
  67. vm_map_entry.next = 0xc1fdfa5c
  68. vm_map_entry.left = 0xc2057220
  69. vm_map_entry.right = 0x0
  70. vm_map_entry.start = 0x28067000
  71. vm_map_entry.end = 0x2806c000
  72. vm_map_entry.avail_ssize = 0x0
  73. vm_map_entry.adj_free = 0
  74. vm_map_entry.max_free = 536866816
  75. vm_map_entry.offset = 0
  76. vm_map_entry.eflags = 0x0


  77. sn = 6
  78. entry addr = 0xc1fdfa5c
  79. vm_map_entry.prev = 0xc1fd0e58
  80. vm_map_entry.next = 0xc209a50c
  81. vm_map_entry.left = 0xc1fd0e58
  82. vm_map_entry.right = 0x0
  83. vm_map_entry.start = 0x2806c000
  84. vm_map_entry.end = 0x28074000
  85. vm_map_entry.avail_ssize = 0x0
  86. vm_map_entry.adj_free = 0
  87. vm_map_entry.max_free = 536866816
  88. vm_map_entry.offset = 20480
  89. vm_map_entry.eflags = 0x0


  90. sn = 7
  91. entry addr = 0xc209a50c
  92. vm_map_entry.prev = 0xc1fdfa5c
  93. vm_map_entry.next = 0xc209a374
  94. vm_map_entry.left = 0xc1fdfa5c
  95. vm_map_entry.right = 0xc209a374
  96. vm_map_entry.start = 0x28074000
  97. vm_map_entry.end = 0x28132000
  98. vm_map_entry.avail_ssize = 0x0
  99. vm_map_entry.adj_free = 0
  100. vm_map_entry.max_free = 536866816
  101. vm_map_entry.offset = 0
  102. vm_map_entry.eflags = 0x404


  103. sn = 8
  104. entry addr = 0xc209a374
  105. vm_map_entry.prev = 0xc209a50c
  106. vm_map_entry.next = 0xc209a3fc
  107. vm_map_entry.left = 0x0
  108. vm_map_entry.right = 0x0
  109. vm_map_entry.start = 0x28132000
  110. vm_map_entry.end = 0x28133000
  111. vm_map_entry.avail_ssize = 0x0
  112. vm_map_entry.adj_free = 0
  113. vm_map_entry.max_free = 0
  114. vm_map_entry.offset = 0
  115. vm_map_entry.eflags = 0x404


  116. sn = 9
  117. entry addr = 0xc209a3fc
  118. vm_map_entry.prev = 0xc209a374
  119. vm_map_entry.next = 0xc209a660
  120. vm_map_entry.left = 0xc209a50c
  121. vm_map_entry.right = 0x0
  122. vm_map_entry.start = 0x28133000
  123. vm_map_entry.end = 0x28138000
  124. vm_map_entry.avail_ssize = 0x0
  125. vm_map_entry.adj_free = 0
  126. vm_map_entry.max_free = 536866816
  127. vm_map_entry.offset = 0
  128. vm_map_entry.eflags = 0x4


  129. sn = 10
  130. entry addr = 0xc209a660
  131. vm_map_entry.prev = 0xc209a3fc
  132. vm_map_entry.next = 0xc209a61c
  133. vm_map_entry.left = 0xc209a3fc
  134. vm_map_entry.right = 0xc209a61c
  135. vm_map_entry.start = 0x28138000
  136. vm_map_entry.end = 0x2814b000
  137. vm_map_entry.avail_ssize = 0x0
  138. vm_map_entry.adj_free = 2544455680
  139. vm_map_entry.max_free = 2544455680
  140. vm_map_entry.offset = 0
  141. vm_map_entry.eflags = 0x0


  142. sn = 11
  143. entry addr = 0xc209a61c
  144. vm_map_entry.prev = 0xc209a660
  145. vm_map_entry.next = 0xc1de9ce4
  146. vm_map_entry.left = 0x0
  147. vm_map_entry.right = 0x0
  148. vm_map_entry.start = 0xbfbe0000
  149. vm_map_entry.end = 0xbfc00000
  150. vm_map_entry.avail_ssize = 0x3fe0000
  151. vm_map_entry.adj_free = 0
  152. vm_map_entry.max_free = 0
  153. vm_map_entry.offset = 0
  154. vm_map_entry.eflags = 0x1000
复制代码


首先来看vm_map结构体中的header:

来看1号entry:

2号entry:

从3号到10号总共8个vm_map_entry分别表示的是8个互相邻接的mmap区域,它们之间并无本质区别,留待后面结合vm_object结构体一起进行探察分析。

11号entry:


[ 本帖最后由 雨丝风片 于 2006-5-10 07:55 编辑 ]
作者: congli    时间: 2006-05-09 16:01
学习ing^_^
作者: miniwei    时间: 2006-05-09 16:31
先收起来~~
过段时间再看了~~
现在看不懂~~
作者: gvim    时间: 2006-05-09 19:24
很有分量,大手笔。
偶抽时间把你的kvm看了再说先
作者: 雨丝风片    时间: 2006-05-09 20:41
原帖由 gvim 于 2006-5-9 19:24 发表
很有分量,大手笔。
偶抽时间把你的kvm看了再说先


抽时间把你的东西也拿出来晾晾先,小心发霉了。
作者: xie_minix    时间: 2006-05-09 22:17
发帖时最不幸的事情是:快写完了,IE缺崩溃了.
写了一大堆后全没了,只有简单的写一点了.
看过你的文章后确实觉得不错,首先我得承认,我从没搞过VM方面的分析
在这方面我肯定是不行的.  
其次我对你文章中的所说的"5.flags的用途尚不清楚。", 我查了下VM的代码
flags和系统调用
324        AUE_NULL        MSTD        { int mlockall(int how); }
有关.是用户区对内存页面加锁用的一个系统调用(见syscalls.master).
该调用有两种方式:一个是MCL_FUTURE,另一个是MCL_CURRENT,
(SYS\SYS\mman.h第96,97行)
当然也可两种并用.他的意思分别是对将要用的页,对当前的内存页加锁,这一点是和LINUX
一样的,当方式是MCL_FUTURE时,flags标志会通过vm_map_modflags
(SYS\VM\vm_map.h地222行)
置位{加入MAP_WIREFUTURE}.当然munlockall系统调用在解锁时也要使用vm_map_modflags
函数去掉flags标志中的MAP_WIREFUTURE.
看看 MCL_FUTURE  的意思(Man Pages ):
Lock all pages mapped into the process's address space in
                  the future, at the time the mapping is established.  Note
                  that this may cause future mappings to fail if those map-
                  pings cause resource limits to be exceeded
而和flags没关系的MCL_CURRENT 的意思(Man Pages )
Lock all pages currently mapped into the process's address
                  space
我认为用户区如果要对连续的将要使用的内存页加锁时会用到flags标志(置MAP_WIREFUTURE)
对于用户进程已经有此标志的堆栈页,如果需要增加堆栈,必须同样注意保持堆栈的连续性.(vm_map.c中
第2899行)
参考:vm_map.c
        vm_mmap.c
        vm_map.h
syscalls.master
等等
好了,不写其他废话了

[ 本帖最后由 xie_minix 于 2006-5-9 23:40 编辑 ]
作者: wangbin    时间: 2006-05-09 23:00
学习!!!!!
作者: eunyoo    时间: 2006-05-10 04:18
LZ知识太高深,偶一点看不懂,严重打击了我的自信心.

估计很多人感觉和我一样.

能否介绍些应用层方面的编程?
作者: 雨丝风片    时间: 2006-05-10 08:55
原帖由 xie_minix 于 2006-5-9 22:17 发表
发帖时最不幸的事情是:快写完了,IE缺崩溃了.
写了一大堆后全没了,只有简单的写一点了.
看过你的文章后确实觉得不错,首先我得承认,我从没搞过VM方面的分析
在这方面我肯定是不行的.  
其次我对你文章中的所说 ...


多谢指点!

我对vm的分析也是源于这里的一个关于top命令的讨论贴,总想搞清楚top输出中的SIZE和RES在“物理上”究竟是什么含义、是如何体现的,于是就一步步地挖了进去。。。

vm_map.c文件目前我也只看过和splay树有关的几个函数,其余的内容还没分析到呢,不过看了你指点的几个函数,比如内存加锁以及堆栈增长,都很有意思,找个时间去仔细看看!

原来还不了解mlockall系统调用,今天大致看了一下内核中的mlockall()函数,其间对vm_map结构体中的flags的设置确如你所说。
  1. _____________________________________________________________________FreeBSD6.0
  2. 938   int
  3. 939   mlockall(td, uap)
  4. 940       struct thread *td;
  5. 941       struct mlockall_args *uap;
  6. 942   {
  7.        ......
  8. 970       if (uap->how & MCL_FUTURE) {
  9. 971           vm_map_lock(map);
  10. 972           vm_map_modflags(map, MAP_WIREFUTURE, 0);
  11. 973           vm_map_unlock(map);
  12. 974           error = 0;
  13. 975       }
  14. ______________________________________________________/usr/src/sys/vm/vm_mmap.c
复制代码


也就是说,只有mlockall系统调用的入参为MCL_FUTURE时才会去设置vm_map的flags,而设置的值就是MAP_WIREFUTURE,即1。如果入参为MCL_CURRENT,则不去设置这个值。

我对此进行了试验,在前面的那个小程序里面增加了mlockall系统调用,然后再去查看flags的值,测试结果与上述分析一致。

另,老大以后还是在vim之类的东西里打草稿吧,否则我们老是只能看到冰山一角,太不爽了。
作者: 雨丝风片    时间: 2006-05-10 10:51
原帖由 eunyoo 于 2006-5-10 04:18 发表
LZ知识太高深,偶一点看不懂,严重打击了我的自信心.
估计很多人感觉和我一样.
能否介绍些应用层方面的编程?


看了你的回帖,心有感触,干脆另开一贴阐述了一下BSD程序开发版的立版宗旨:

http://bbs.chinaunix.net/viewthr ... &extra=page%3D1

欢迎多去给BSD程序开发版提一些意见和建议!具体到介绍应用层方面的编程,能不能具体谈一些要求,我们好做安排。
作者: 黑夜编码人    时间: 2006-05-10 11:51
好文,收藏!
作者: ljoolj    时间: 2006-05-12 14:48
各位高手,程序崩溃后的内核转储的文件如何分析呢?
作者: 雨丝风片    时间: 2006-05-12 16:52
原帖由 ljoolj 于 2006-5-12 14:48 发表
各位高手,程序崩溃后的内核转储的文件如何分析呢?


这个问题是一个比较普遍的问题,我另外开了一个帖子,写了一个简单的例子供讨论:
http://bbs.chinaunix.net/viewthr ... page%3D1#pid5145879




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2