linux进程地址空间布局,linux下c程序进程地址空间布局

62e50291a81fc54b507f33cb80033297.gif linux下c程序进程地址空间布局

(14页)

9836e53f69f5d2ca45ac8f695bc03deb.gif

本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦!

8.90 积分

我们在学习 C 程序开发时经常会遇到一些概念:代码段、数据段、BSS 段(Block Started by Symbol) 、堆(heap)和栈(stack)。先看一张教材上的示意图(来源,《UNIX 环境高级编程》一书),显示了进程地址空间中典型的存储区域分配情况。从图中可以看出:?从低地址到高地址分别为:代码段、(初始化)数据段、(未初始化)数据段(BSS)、堆、栈、命令行参数和环境变量?堆向高内存地址生长?栈向低内存地址生长还经常看到下面这个图(来源,不详):先看一段程序。[cpp] view plaincopyprint?1.#include 2.#include 3. 4.int global_init_a=1; 5.int global_uninit_a; 6.static int static_global_init_a=1; 7.static int static_global_uninit_a; 8.const int const_global_a=1; 9. 10. int global_init_b=1; 11. int global_uninit_b; 12. static int static_global_init_b=1; 13. static int static_global_uninit_b; 14. const int const_global_b=1; 15. /*上面全部为全局变量,main 函数中的为局部变量*/ 16. int main() 17. { 18. int local_init_a=1; 19. int local_uninit_a; 20. static int static_local_init_a=1; 21. static int static_local_uninit_a; 22. const int const_local_a=1; 23. 24. int local_init_b=1; 25. int local_uninit_b; 26. static int static_local_init_b=1; 27. static int static_local_uninit_b; 28. const int const_local_b=1; 29. 30. int * malloc_p_a; 31. malloc_p_a=malloc(sizeof(int)); 32. 33. printf(“\n 35. 36. printf(“ 38. 39. printf(“ 41. 42. printf(“ 44. 45. printf(“ 47. 48. 49. printf(“\n 51. 52. printf(“ 54. 55. printf(“ 57. 58. printf(“ 60. 61. printf(“ 63. 64. 65. 66. printf(“\n 68. 69. printf(“ 71. 72. printf(“ 74. 75. printf(“ 77. 78. printf(“ 80. 81. 82. printf(“\n 84. 85. printf(“ 87. 88. printf(“ 90. 91. printf(“ 93. 94. printf(“ 96. 97. 98. printf(“ malloc_p_a=%p \t 99. *malloc_p_a=%d\n“,malloc_p_a,*malloc_p_a); 100. 101. return 0; 102.} 下面是输出结果。先仔细分析一下上面的输出结果,看看能得出什么结论。貌似很难分析出来什么结果。好了我们继续往下看吧。接下来,通过查看 proc 文件系统下的文件,看一下这个进程的真实内存分配情况。(我们需要在程序结束前加一个死循环,不让进程结束,以便我们进一步分析)。在 return 0 前,增加 while(1); 语句重新编译后,运行程序,程序将进入死循环。重新编译后,运行程序,程序将进入死循环。使用使用 ps 命令查看一下进程的命令查看一下进程的 pid#ps -aux | grep a.out查看查看/proc/2699/maps 文件,这个文件显示了进程在内存空间中各个区域的分配情况。文件,这个文件显示了进程在内存空间中各个区域的分配情况。#cat /proc/2699/maps上面红颜色标出的几个区间是我们感兴趣的区间:?08048000-08049000 r-xp 貌似是代码段?08049000-0804a000 r--p 暂时不清楚,看不出来?0804a000-0804b000 rw-p 貌似为数据段?08a7e000-08a9f000 rw-p 堆?bff73000-bff88000 rw-p 栈 我们把这些数据与最后一次的程序运行结果进行比较,看看有什么结论。&global_init_a=0x804a018 全局初始化:数据段 global_init_a=1&global_uninit_a=0x804a04c 全局未初始化:数据段 global_uninit_a=0&static_global_init_a=0x804a01c 全局静态初始化:数据段 static_global_init_a=1& static_global_uninit_a=0x804a038 全局静态未初始化:数据段 static_global_uninit_a=0&const_global_a=0x80487c0 全局只读变量: 代码段 const_global_a=1&global_init_b=0x804a020 全局初始化:数据段 global_init_b=1&global_uninit_b=0x804a048 全局未初始化:数据段 global_uninit_b=0&static_global_init_b=0x804a024 全局静态初始化:数据段 static_global_init_b=1& static_global_uninit_b=0x804a03c 全局静态未初始化:数据段 static_global_uninit_b=0&const_global_b=0x80487c4 全局只读变量: 代码段 const_global_b=1&local_init_a=0xbff8600c 局部初始化:栈 local_init_a=1&local_uninit_a=0xbff86008 局部未初始化:栈 local_uninit_a=134514459&static_local_init_a=0x804a028 局部静态初始化:数据段 static_local_init_a=1&static_local_uninit_a=0x804a040 局部静态未初始化:数据段 static_local_uninit_a=0&const_local_a=0xbff86004 局部只读变量:栈 const_local_a=1&local_init_b=0xbff86000 局部初始化:栈 local_init_b=1&local_uninit_b=0xbff85ffc 局部未初始化:栈 local_uninit_b=-1074241512&static_local_init_b=0x804a02c 局部静态初始化:数据段 static_local_init_b=1&static_local_uninit_b=0x804a044 局部静态未初始化:数据段 static_local_uninit_b=0&const_local_b=0xbff85ff8 局部只读变量:栈 const_local_b=1p_chars=0x80487c8 字符串常量:代码段 p_chars=abcdefmalloc_p_a=0x8a7e008 malloc 动态分配:堆 *malloc_p_a=0通过以上分析我们暂时可以得到的结论如下,在进程的地址空间中在进程的地址空间中:?数据段中存放:全局变量(初始化以及未初始化的)、静态变量(全局的和局数据段中存放:全局变量(初始化以及未初始化的)、静态变量(全局的和局部的、初始化的以及未初始化的)部的、初始化的以及未初始化的)?代码段中存放:全局只读变量(代码段中存放:全局只读变量(const)、字符串常量)、字符串常量?堆中存放:动态分配的区域堆中存放:动态分配的区域?栈中存放:局部变量(初始化以及未初始化的,但不包含静态变量)、局部只栈中存放:局部变量(初始化以及未初始化的,但不包含静态变量)、局部只读变量(读变量(const))这里我们没有发现 BSS 段,但是我们将未初始化的数据按照地址进行排序看一下,可以发现一个规律。&global_init_a=0x804a018 全局初始化:数据段 global_init_a=1&static_global_init_a=0x804a01c 全局静态初始化:数据段 static_global_init_a=1&global_init_b=0x804a020 全局初始化:数据段 global_init_b=1&static_global_init_b=0x804a024 全局静态初始化:数据段 static_global_init_b=1&static_local_init_a=0x804a028 局部静态初始化:数据段 static_local_init_a=1&static_local_init_b=0x804a02c 局部静态初始化:数据段 static_local_init_b=1& static_global_uninit_a=0x804a038 全局静态未初始化:数据段 static_global_uninit_a=0& static_global_uninit_b=0x804a03c 全局静态未初始化:数据段 static_global_uninit_b=0&static_local_uninit_a=0x804a040 局部静态未初始化:数据段 static_local_uninit_a=0&static_local_uninit_b=0x804a044 局部静态未初始化:数据段 static_local_uninit_b=0&global_uninit_b=0x804a048 全局未初始化:数据段 global_uninit_b=0&global_uninit_a=0x804a04c 全局未初始化:数据段 global_uninit_a=0这里可以发现,初始化的和未初始化的数据好像是分开存放的初始化的和未初始化的数据好像是分开存放的,因此我们可以猜测BSS 段是存在的,只不过数据段是分为初始化和未初始化(即 BSS 段)的两部分,他们在加载到进程地址空间时是合并为数据段了,在进程地址空间中没有单独分为一个区域。还有一个问题,静态数据与非静态数据是否是分开存放的呢?请读者自行分析一下。接下来我们从程序的角度程序的角度看一下,这些存储区域是如何分配的。首先我们先介绍一下 ELF文件格式。ELF(Executable and Linkable Format )文件格式是一个开放标准,各种 UNIX 系统的可执行文件都采用 ELF 格式,它有三种不同的类型:–可重定位的目标文件(Relocatable,或者 Object File)–可执行文件(Executable)–共享库(Shared Object,或者 Shared Library)下图为 ELF 文件的结构示意图(来源,不详):一个程序编译生成目标代码文件(ELF 文件)的过程如下,此图引自《程序员的自我修养》一书的一个图:可以通过 readelf 命令查看 EFL 文件的相关信息,例如 readelf -a a.out ,我们只关心各个段的分配情况,因此我们使用以下命令:# readelf -S a.out将这里的内存布局与之前看到的程序的运行结果进行分析:&global_init_a=0x804a018 全局初始化:数据段 global_init_a=1&global_uninit_a=0x804a04c 全局未初始化:BSS 段 global_uninit_a=0&static_global_init_a=0x804a01c 全局静态初始化:数据段 static_global_init_a=1&static_global_uninit_a=0x804a038 全局静态未初始化:BSS 段 static_global_uninit_a=0&const_global_a=0x80487c0 全局只读变量: 只读数据段 const_global_a=1&global_init_b=0x804a020 全局初始化:数据段 global_init_b=1&global_uninit_b=0x804a048 全局未初始化:BSS 段 global_uninit_b=0&static_global_init_b=0x804a024 全局静态初始化:数据段 static_global_init_b=1&static_global_uninit_b=0x804a03c 全局静态未初始化:BSS 段 static_global_uninit_b=0&const_global_b=0x80487c4 全局只读变量: 只读数据段 const_global_b=1&static_local_init_a=0x804a028 局部静态初始化:数据段 static_local_init_a=1&static_local_uninit_a=0x804a040 局部静态未初始化:BSS 段 static_local_uninit_a=0&static_local_init_b=0x804a02c 局部静态初始化:数据段 static_local_init_b=1&static_local_uninit_b=0x804a044 局部静态未初始化:BSS 段 static_local_uninit_b=0p_chars=0x80487c8 字符串常量:只读数据段 p_chars=abcdefELF 文件一般包含以下几个段 :?.text section:主要是编译后的源码指令,是只读字段。:主要是编译后的源码指令,是只读字段。?.data section :初始化后的非:初始化后的非 const 的全局变量、局部的全局变量、局部 static 变量。变量。?.bss:未初始化后的非:未初始化后的非 const 全局变量、局部全局变量、局部 static 变量。变量。?.rodata 字段字段 是存放只读数据是存放只读数据 分析到这以后,我们在和之前分析的结果对比一下,会发现确实存在会发现确实存在 BSS 段段,地址为0804a030 ,大小为 0x20,之前我们的程序中未初始化的的确存放在这个地址区间中了,只不过执行 exec 系统调用时,将这部分的数据初始化为 0 后,放到了进程地址空间的数据段中了,在进程地址空间中就没有必要存在 BSS 段了,因此都称做数据段。同理,.rodata 字段也是与 text 段放在一起了。在 ELF 文件中,找不到局部非静态变量和动态分配的内容。以上有很多地方的分析,我在网上基本找不到很明确的结论,很多教材上也没有描述,只是我通过程序分析得出的结论,如有不妥之处,请指出,欢迎交流。 关 键 词: linux 程序 进程 地址 空间布局

524d6daf746efaa52c3c71bbfe7ba172.gif  天天文库所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值