11.执行阶段

本文详细介绍了C语言程序在Linux下的内存布局,包括代码段、数据段、运行时堆、共享库段及用户栈等各部分的作用及位置。通过具体示例代码展示了不同类型的变量在内存中的分配情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

11.执行阶段: 运行时内存模型

一个普通的C程序经过预处理器、编译器、汇编器和链接器后生成一个可执行的目标文件,它由最初的一段ASCII文本文件转化成为一个二进制文件,且这个二进制文件包含加载程序到内存并运行它所需的所有信息。

 C运行时内存模型

进程在运行时为C程序提供了一个通用的运行时存储器映像。    


Linux将这个运行时存储器映像组织成若干段的集合,它主要有两部分:进程虚拟存储器、内核虚拟存储器。进程虚拟存储器有我们熟悉的代码段、数据段、运行时堆、共享库段、用户栈。内核虚拟存储器包括内核中的代码和数据结构、与进程相关的数据结构。以32位系统的可执行文件的运行时存储器映像来说:

l  代码段总是从地址0x08048000处开始,它保存编译程序的机器代码

l  data段在接下来的一个4KB对齐的地址处,保存已初始化的全局C变量和静态变量

l  bss段记录的是未初始化的全局C变量,事实上它并不占据目标文件的任何空间,只是一个占位符

l  运行时堆在接下来的第一个4KB对齐的地址处,通过调用malloc库向上增长,用于程序的动态内存管理

l  共享库段,用于加载共享库、映射共享内存和文件I/O,使用mmap和unmap函数申请和释放新的内存区

l  用户栈占据进程地址空间的最高部分,并向下增长,用于存放调用过程中的局部变量、函数返回地址、参数

l  内核代码和数据、物理存储器,它们均被映射到所有进程共享的物理页面,这就为内核提供一个便利的方法来访问内存中任何特定的位置。对于每个进程来说他们均是一样的

l  最顶层的内核地址空间包括了与进程有关的数据结构,如页表、内核在进程的上下文结构task_struct和mm结构,内核栈

代码测试如下:

#include <stdio.h>

#include <stdlib.h>

int glob1=120;

int glob2;

extern int etext,edata,end;

int func2(){

    intf2_local1,f2_local2;

   printf("函数2的局部变量-\tf2_local1: %p  \tf2_local2:%p\n",&f2_local1,&f2_local2);

}

int func1(){

    intf1_local1,f1_local2;

   printf("函数1的局部变量-\tf1_local1: %p  \tf1_local2:%p\n",&f1_local1,&f1_local2);

    func2();

}

int main(){

    intm_local1,m_local2;

    int*dyn_addr;

   printf("代码段的结束地址:%p\n",&etext);

   printf("初始化数据区的结束地址:%p\n",&edata);

   printf("未初始化数据区的结束地址:%p\n",&end);

   printf("运行时初始可用堆区域的边界地址:%p\n\n",(char *)sbrk(0));

   printf("全局变量- glob1:%p \t glob2:%p\n",&glob1,&glob2);

   dyn_addr=(int *)malloc(16);

   printf("指针变量的值在堆区域-dyn_addr: %p\n",dyn_addr);

   printf("函数地址-\t main:%p \tfunc1: %p  \tfunc2:%p\n",main,func1,func2);

   printf("main函数的局部变量-\tm_local1: %p  \tm_local2:%p\n",&m_local1,&m_local2);

    func1();

    return 0;

}

输出如下:


结果看出这是一个运行在64位机器上的程序(64位的代码段总是从0x400000),函数地址main,func1,func2的地址均在代码区域中,全局变量glob1的地址在初始化数据区中,全glob2的地址在未初始化数据区中,指针遍历dyn_addr的值在堆区域中,局部变量m_local1、m_local2、f1_local1、f1_local2、f2_local1、f2_local2依次存放在栈(并且可知栈的增长方向是从高地址到低地址,堆的增长方向是从低地址到高地址)

一个普通的C程序经过预处理器、编译器、汇编器和链接器后生成一个可执行的目标文件,它由最初的一段ASCII文本文件转化成为一个二进制文件,且这个二进制文件包含加载程序到内存并运行它所需的所有信息。

 C运行时内存模型

进程在运行时为C程序提供了一个通用的运行时存储器映像。    

Linux将这个运行时存储器映像组织成若干段的集合,它主要有两部分:进程虚拟存储器、内核虚拟存储器。进程虚拟存储器有我们熟悉的代码段、数据段、运行时堆、共享库段、用户栈。内核虚拟存储器包括内核中的代码和数据结构、与进程相关的数据结构。以32位系统的可执行文件的运行时存储器映像来说:

l  代码段总是从地址0x08048000处开始,它保存编译程序的机器代码

l  data段在接下来的一个4KB对齐的地址处,保存已初始化的全局C变量和静态变量

l  bss段记录的是未初始化的全局C变量,事实上它并不占据目标文件的任何空间,只是一个占位符

l  运行时堆在接下来的第一个4KB对齐的地址处,通过调用malloc库向上增长,用于程序的动态内存管理

l  共享库段,用于加载共享库、映射共享内存和文件I/O,使用mmap和unmap函数申请和释放新的内存区

l  用户栈占据进程地址空间的最高部分,并向下增长,用于存放调用过程中的局部变量、函数返回地址、参数

l  内核代码和数据、物理存储器,它们均被映射到所有进程共享的物理页面,这就为内核提供一个便利的方法来访问内存中任何特定的位置。对于每个进程来说他们均是一样的

l  最顶层的内核地址空间包括了与进程有关的数据结构,如页表、内核在进程的上下文结构task_struct和mm结构,内核栈

代码测试如下:

#include <stdio.h>

#include <stdlib.h>

int glob1=120;

int glob2;

extern int etext,edata,end;

int func2(){

    intf2_local1,f2_local2;

   printf("函数2的局部变量-\tf2_local1: %p  \tf2_local2:%p\n",&f2_local1,&f2_local2);

}

int func1(){

    intf1_local1,f1_local2;

   printf("函数1的局部变量-\tf1_local1: %p  \tf1_local2:%p\n",&f1_local1,&f1_local2);

    func2();

}

int main(){

    intm_local1,m_local2;

    int*dyn_addr;

   printf("代码段的结束地址:%p\n",&etext);

   printf("初始化数据区的结束地址:%p\n",&edata);

   printf("未初始化数据区的结束地址:%p\n",&end);

   printf("运行时初始可用堆区域的边界地址:%p\n\n",(char *)sbrk(0));

   printf("全局变量- glob1:%p \t glob2:%p\n",&glob1,&glob2);

   dyn_addr=(int *)malloc(16);

   printf("指针变量的值在堆区域-dyn_addr: %p\n",dyn_addr);

   printf("函数地址-\t main:%p \tfunc1: %p  \tfunc2:%p\n",main,func1,func2);

   printf("main函数的局部变量-\tm_local1: %p  \tm_local2:%p\n",&m_local1,&m_local2);

    func1();

    return 0;

}

输出如下:

结果看出这是一个运行在64位机器上的程序(64位的代码段总是从0x400000),函数地址main,func1,func2的地址均在代码区域中,全局变量glob1的地址在初始化数据区中,全glob2的地址在未初始化数据区中,指针遍历dyn_addr的值在堆区域中,局部变量m_local1、m_local2、f1_local1、f1_local2、f2_local1、f2_local2依次存放在栈(并且可知栈的增长方向是从高地址到低地址,堆的增长方向是从低地址到高地址)

参考书籍

Memory management in C: The heap and the stack

 参考链接

http://www.cnblogs.com/xionghj/p/4319485.html

http://www.cnblogs.com/xionghj/p/4319463.html

http://www.cnblogs.com/xionghj/p/4319504.html







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值