Chapter 3 User-Level Memory Managment
3.1 Linux/Unix 地址空间
众所周知,操作系统把一个可执行的程序载入内存中,这个可执行程序可称为一个进程。操作系统为程序访问命令行和环境变量做好准备,将程序开启运行。分配进程的内存可分为以下五个区域:
①代码段 (文本段-text segment)
可执行指令放在这个区域。如果可能,Linux/Unix会安排好相同程序的多个运行实体共享这些实例代码。任何内存只有一份相同程序的指令拷贝。可执行文件中包含文本段的部分叫文本结点(text section)。
②数据段 (data segment)
初始化为非零值的静态分配数据和全局数据存放在数据段中。运行相同程序的每个进程都有自己的数据段。可执行文件中包含数据段的部分是数据结点(data section)。
③BSS 段(Block Started by Symbol)
缺省初始化为零的全局数据和静态分配的数据存放在进程的BSS区域。每个运行相同程序的进程都有自己的BSS区域。当程序运行的时候,把BSS数据放到数据段。BSS数据存放在BSS节当中。
④堆 (Heap)
用于分配动态内存 (由malloc()等一组函数得到)。当在堆上分配内存的时候,进程地址空间将增大。(可有ps命令查看)。一般情况下,堆是向上增长的。
⑤栈 (Stack)
堆栈段(stack segment)用于分配本地变量(local invarible)的地方。本地变量是声明在函数体开始左大括号内的变量(或其它左大括号),不可以定义成static变量。
大部分体系中,函数的参数也放在堆栈里,还有编译器产生的不可见的薄记信息,例如薄记信息中有存放函数返回值和返回地址的存储空间。正是在函数参数和返回值上使用了堆栈才使递归函数的编写变得方便了。当函数返回的时候,存储在堆栈里的函数变量消失了,堆栈里的空间又可以重新用于后面的函数调用。大部分体系中,堆栈是向下增长的。
当程序运行的时候,初始化数据、BSS和堆栈区域通常放在一个单独的连续区域:数据段。堆栈段和代码段与数据段是分隔开的。
下面为了证实地址空间的各个区域的确存在,给出如下测试程序:
-------------------------------------------------------------------CODE----------------------------------------------------------------------
/*
* ch03-memaddr.c--------------显示代码段、数据段、堆栈段的地址,
* 以及BSS段和动态内存的地址
*/
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <alloca.h>
extern void afunc( void );
int bss_var;
int data_var=42;
int
main(int argc, char** argv)
{
char *p, *b, *nb;
printf("/tAddress of main :%p/n", main);
printf("/taddress of afunc:%p/n", afunc);
printf("Stack Loactions:/n);
afunc( ) ;
p=( char * ) alloca( 12 );
if ( p !=NULL) {
printf("/tStack of alloca( )'e array : %p/n", p );
printf("/tEnd of alloca( )'e array : %p/n", p+31 );
}
printf("Data Locations : /n");
printf("/tAddress of data_var : %p/n", &data_var );
printf("BSS Locations:/n");
printf("/tAddress of bss_var: %p/n", &bss_var) ;
b = sbrk( ( ptrdiff_t ) 32 ); /* 扩大此程序的地址空间 */
nb = sbrk( ( ptrdiff_t ) 0); /* 得到扩大了的地址空间目前结束的地方 */
printf("Heap Locations: /n ");
printf( "/tInitial end of heap:%p/n", b );
printf( "/tNew end of heap : %p/n", nb );
b = sbrk ( ( ptrdiff_t ) -16 ); /* 缩小此程序的地址空间 */
nb = sbrk ( ( ptrdiff_t ) 0 ); /* 得到缩小了的地址空间目前结束的地方 */
printf ( "/t Final end of heap : %p/n, nb );
}
void
afunc( void )
{
static int level = 0;
auto int stack_var;
if ( ++level == 3)
return;
printf ( " /tStack level %d : address of stack_var : %p/n", level, & stack_var );
afunc();
}
---------------------------------------------------CODE END-------------------------------------------------------------------------------
在Linux系统下,或者在Windows 的 Cygwin 下用GCC编译并执行此程序得如下结果:
____________________________________________________________________________________
$gcc -o ch03-memaddr ch03-memaddr.c
$ ./ch03-memaddr
Text Locations:
Address of main: 0x401050
Address of afun: 0x4011c4
Stack Locations:
Stack level 1 : address of stack_var: 0x22eef4
Stack level 2 : address of stack_var: 0x22eed4 栈向下增长
Stack of alloca()e array : 0x22ee0;
End of alloca()e array : 0x22eeff 地址在栈上
Dara Locations:
Address of data_var : 0x402000
BSS Locations:
Address of bss_var : 0x404030 BSS 段变量在Data段变量之上
Heap Locations :
Initial end of heap : 0x4b1000 Heap 紧接着在BSS段之上
New end of heap : 0x4b1020 并且 Heap 向上增长
Final end of heap : 0x4b1010 地址空间可以收缩____________________________________________________________________________________