Chinaunix首页 | 论坛 | 博客
  • 博客访问: 236993
  • 博文数量: 77
  • 博客积分: 80
  • 博客等级: 民兵
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-06 17:15
文章分类
文章存档

2013年(4)

2012年(73)

分类:

2012-04-13 21:12:46

原文地址:深入理解C程序内存布局 作者:

1、堆和栈的区别,堆和栈的最大限制

    堆主要用来分配动态内存,操作系统提供了malloc等内存分配机制来供程序员进行堆内存的分配,同时,堆内存的释放需要程序员来进行。malloc分配的是虚拟地址空间,和用到的实实在在的物理内存是两码事,只有真正往空间里写东西了,os内核会触发缺页异常,然后才真正得到物理内存。32位Linux系统总共有4G内存空间,Linux把最高的1G(0xC0000000-0xFFFFFFFF)作为内核空间,把低地址的3G(0x00000000-0xBFFFFFFF)作为用户空间。malloc函数在这3G的用户空间中分配内存,能分配到的大小不超过3G,需除去栈、数据段(初始化及未初始化的)、共享so及代码段等占的内存空间。
堆的地址空间是由低向高增长的(先分配低地址)。我用以下程序进行测试:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main(int argc, char *argv[])
  4. {
  5.      char *ch = NULL;
  6.      unsigned int size = 2147 * 1000000;
  7.      ch = (char *)malloc(size);
  8.      if(ch == NULL)
  9.          perror("malloc failed\n");
  10.      else
  11.          printf("malloc success\n");
  12.      free(ch);
  13.      return 0;
  14. }
发现最大能分配的内存约为2.147GB。
为什么这么说: 我们可以看malloc函数的原型void *malloc(size_t size); size_t 在stddef.h里定义的是unsigned int类型,故在ilp32平台上其最大取值是2147483647

   栈由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。栈的地址空间是由高向低减少的(先分配高地址)。在Linux中,用ulimit -a命令可以看到栈的最大分配空间(stack size)是8192kB,即8MB多。

 2、Linux中进程最大地址空间

Linux的虚拟地址空间也为0~4G。Linux内核将虚拟的4G字节的空间分为两部分。
将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为"内核空间"。将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为"用户空间"。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。
其中,很重要的一点是虚拟地址空间,并不是实际的地址空间。进程地址空间是用多少分配多少,4G仅仅是最大限额而已。往往,一个进程的地址空间总是小于4G的,你可以通过查看/proc/pid/maps文件来获悉某个具体进程的地址空间。但进程的地址空间并不对应实际的物理页,Linux采用Lazy的机制来分配实际的物理页("Demand paging"和"和写时复制(Copy On Write)的技术"),从而提高实际内存的使用率。即每个虚拟内存页并不一定对应于物理页。虚拟页和物理页的对应是通过映射的机制来实现的,即通过页表进行映射到实际的物理页。因为每个进程都有自己的页表,因此可以保证不同进程从上到下(地址从高到低)依次为栈(函数内部局部变量),堆(动态分配内存) ,bss段(存未初始化的全局变量),数据段(存初始化的全局变量),文本段(存代码)同虚拟地址可以映射到不同的物理页,从而为不同的进程都可以同时拥有4G的虚拟地址空间提供了可能。

3、C/C++程序在Linux中的内存布局

用以下程序进行测试

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. char bss_1[40];
  4. static double bss_2;
  5. int data_1 = 13;
  6. static long data_2 = 2001; 

  7. int main(int argc, char *argv[])
  8. {
  9.     int stack_1 = 3, stack_2, *heap_1, *heap_2;
  10.     heap_1 = malloc(sizeof(stack_1));
  11.     heap_2 = malloc(sizeof(stack_1));
  12.     bss_1[5] = stack_1;
  13.     bss_2 = 2.0 * data_1;
  14.     printf("stack segment: stack_1:%p, stack_2:%p\n", &stack_1, &stack_2);
  15.     printf("heap segment: heap_1:%p, heap_2:%p\n", heap_1, heap_2);
  16.     printf("bss segment: bss_1:%p, bss_2:%p\n", bss_1, &bss_2);
  17.     printf("data segment: data_1:%p, data_2:%p\n", &data_1, &data_2);
  18.     printf("the stack top is near %p\n", &stack_1);
  19.     return 0;
  20. }
运行结果:
stack segment: stack_1:0xbfab012c, stack_2:0xbfab0128
stack segment: stack_1:0xbfb9e2ec, stack_2:0xbfb9e2e8
heap segment: heap_1:0x8c56008, heap_2:0x8c56018
bss segment:  bss_1:0x804a040, bss_2:0x804a028
data segment: data_1:0x804a018, data_2:0x804a01c
the stack top is near 0xbfb9e2ec

由此可见:从上到下(地址从高到低)依次为栈(函数内部局部变量),动态链接库,堆(动态分配内存),bss段(存未初始化的全局变量),数据段(存初始化的全局变量),文本段(存代码)


4、在 linux 下,程序运行时空间的分配情况:
#cat /proc/self/maps

点击(此处)折叠或打开

  1. ===============================================================================
  2. 00110000-00267000 r-xp 00000000 08:08 406311 /lib/libc-2.12.1.so
  3. 00267000-00269000 r--p 00157000 08:08 406311 /lib/libc-2.12.1.so
  4. 00269000-0026a000 rw-p 00159000 08:08 406311 /lib/libc-2.12.1.so
  5. 0026a000-0026d000 rw-p 00000000 00:00 0
  6. 0038f000-00390000 r-xp 00000000 00:00 0 [vdso]
  7. 0040e000-0042a000 r-xp 00000000 08:08 402927 /lib/ld-2.12.1.so
  8. 0042a000-0042b000 r--p 0001b000 08:08 402927 /lib/ld-2.12.1.so
  9. 0042b000-0042c000 rw-p 0001c000 08:08 402927 /lib/ld-2.12.1.so
  10. 08048000-08051000 r-xp 00000000 08:08 390934 /bin/cat
  11. 08051000-08052000 r--p 00008000 08:08 390934 /bin/cat
  12. 08052000-08053000 rw-p 00009000 08:08 390934 /bin/cat
  13. 09058000-09079000 rw-p 00000000 00:00 0 [heap]
  14. b740f000-b75a2000 r--p 002a3000 08:08 130931 /usr/lib/locale/locale-archive
  15. b75a2000-b77a2000 r--p 00000000 08:08 130931 /usr/lib/locale/locale-archive
  16. b77a2000-b77a3000 rw-p 00000000 00:00 0
  17. b77b5000-b77b7000 rw-p 00000000 00:00 0
  18. bf987000-bf9a8000 rw-p 00000000 00:00 0 [stack]
  19. ===============================================================================
解释:
每个区间都有四个属性:
r 表示可以读取。
w 表示可以修改。
x 表示可以执行。
p/s 表示是否为共享内存。
有文件名的内存区间,属性为 r—p 表示存放的是 rodata。
有文件名的内存区间,属性为 rw-p 表示存放的是 bss 和 data
有文件名的内存区间,属性为 r-xp 表示存放的是 text 数据。
没有文件名的内存区间,表示用 mmap 映射的匿名空间。
文件名为[stack]的内存区间表示是栈。
文件名为[heap]的内存区间表示是堆。
阅读(710) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~