标签:
环境:Linux,redhat
当一段程序被编译成为一个可执行的文件时,这个时候它已经被划分成代码段、数据段、栈段、.bss段、堆等部分。
各段的作用是:
1.代码段(.text):代码,全局常量(const),只读变量和字符串常量(有可能在代码段,一般被放在只读数据".rodata"段,还有可能就在“.data”段)。
2.数据段(.data):全局变量(初始化以及未初始化的)、静态变量(全局的和局部的、初始化的以及未初始化的)。数据段包括初始化的数据和未初始化的数据(BSS)两部分。BSS段存放的是未初始化的全局变量和静态变量。BSS段在不占可执行文件空间,文件只记录BSS段的在内存中的开始和结束地址。
3.堆:动态分配的区域。
4.栈:局部变量(初始化以及未初始化的,但不包含静态变量)、局部只读变量(const)。
为什么需要分段呢,这是有理由的,总的来说,第一点是为了将程序的层次划分清晰,不过这点似乎不是最重要的原因才说得过去;第二点,① 由编译器负责挑选出数据具备的属性,从而根据属性将程序片段分类,比如,划分出了只读属性的代码段和可写属性的数据段;② 由内核分配段的属性(例如读、写、执行等); ③ 这时候就可以将段描述符写进CPU的段寄存器中了,这就相当于操作系统定义段,然后要告知CPU,操作系统对段做了些什么,好让CPU做出相应的准备(例如只读段,CPU就不能去写,写操作就会产生异常)。
具体的讲解在我转载的这篇文章中已经说的很是详细了。http://my.oschina.net/yuyounglife/blog/706376
这里我拿几段重要的话出来。
做过开发的同学都清楚,尽量把同一属性的数据放在一起,这样易于维护。这一点类似于MVC,在程序逻辑中把模型、视图、控制这三部分分开,这样更新各部分时,不会影响到其他模块。
将数据和代码分开的好处有三点。
第一,可以为它们赋予不同的属性。
例如数据本身是需要修改的,所以数据就需要有可写的属性,不让数据段可写,那程序根本就无法执行啦。程序中的代码是不能被更改的,这样就要求代码段具备只读的属性。真要是在运行过程中程序的下一条指令被修改了,谁知道会产生什么样的灾难。
第二,为了提高CPU内部缓存的命中率。
大伙儿知道,缓存起作用的原因是程序的局部性原理。在CPU内部也有缓存机制,将程序中的指令和数据分离,这有利于增强程序的局部性。CPU内部有针对数据和针对指令的两种缓存机制,因此,将数据和代码分开存储将使程序运行得更快。
第三,节省内存。
程序中存在一些只读的部分,比如代码,当一个程序的多个副本同时运行时(比如同时执行多个ls命令时),没必要在内存中同时存在多个相同的代码段,这将浪费有限的物理内存资源,只要把这一个代码段共享就可以了。
/* 国嵌代码addr.c */
#include<stdio.h>
#include<stdlib.h>
int global_init_a = 0; // 全局 初始化的变量 数据段
int global_uninit_a; // 全局 未初始化 BSS段
static int static_global_init_a = 1; // 全局 静态 初始化 数据段
static int static_global_uninit_a; // 全局 静态 未初始化 BSS段
const int const_global_a=1; // 全局 常量 代码段
int global_init_b = 0; // 全局 初始化 数据段
int global_uninit_b; // 全局 未初始化 BSS段
static int static_global_init_b = 1; // 全局 静态 初始化 数据段
static int static_global_uninit_b; // 全局 静态 未初始化 BSS段
const int const_global_b=1; // 全局 常量 代码段
int main()
{
int local_init_a = 1; // 局部 初始化 栈
int local_uninit_a; // 局部 未初始化 栈
static int static_local_init_a = 1; // 局部 静态 初始化 数据段
static int static_local_uninit_a; // 局部 静态 未初始化 BSS段
const int const_local_a = 1; // 局部 常量 栈
int local_init_b = 1; // 局部 初始化 栈
int local_uninit_b; // 局部 未初始化 栈
static int static_local_init_b = 1; // 局部 静态 初始化 数据段
static int static_local_uninit_b; // 局部 静态 未初始化 BSS段
const int const_local_b = 1; // 局部 常量 栈
int * malloc_p_a; // malloc分配得到的局部 堆
malloc_p_a = malloc(sizeof(int));
printf("&global_init_a=%p, global_init_a=%d\n", &global_init_a, global_init_a);
printf("&global_uninit_a=%p, global_uninit_a=%d\n", &global_uninit_a, global_uninit_a);
printf("&static_global_init_a=%p, static_global_init_a=%d\n", &static_global_init_a, static_global_init_a);
printf("&static_global_uninit_a=%p, static_global_uninit_a=%d\n", &static_global_uninit_a, static_global_uninit_a);
printf("&const_global_a=%p, const_global_a=%d\n", &const_global_a, const_global_a);
printf("&global_init_b=%p, global_init_b=%d\n", &global_init_b, global_init_b);
printf("&global_uninit_b=%p, global_uninit_b=%d\n", &global_uninit_b, global_uninit_b);
printf("&static_global_init_b=%p, static_global_init_b=%d\n", &static_global_init_b, static_global_init_b);
printf("&static_global_uninit_b=%p, static_global_uninit_b=%d\n", &static_global_uninit_b, static_global_uninit_b);
printf("&const_global_b=%p, const_global_b=%d\n", &const_global_b, const_global_b);
printf("&local_init_a=%p, local_init_a=%d\n", &local_init_a, local_init_a);
printf("&local_uninit_a=%p, local_uninit_a=%d\n", &local_uninit_a, local_uninit_a);
printf("&static_local_init_a=%p, static_local_init_a=%d\n", &static_local_init_a, static_local_init_a);
printf("&static_local_uninit_a=%p, static_local_uninit_a=%d\n", &static_local_uninit_a, static_local_uninit_a);
printf("&const_local_a=%p, const_local_a=%d\n", &const_local_a, const_local_a);
printf("&local_init_b=%p, local_init_b=%d\n", &local_init_b, local_init_b);
printf("&local_uninit_b=%p, local_uninit_b=%d\n", &local_uninit_b, local_uninit_b);
printf("&static_local_init_b=%p, static_local_init_b=%d\n", &static_local_init_b, static_local_init_b);
printf("&static_local_uninit_b=%p, static_local_uninit_b=%d\n", &static_local_uninit_b, static_local_uninit_b);
printf("&const_local_b=%p, const_local_b=%d\n", &const_local_b, const_local_b);
printf("&malloc_p_a=%p, *malloc_p_a=%d\n", malloc_p_a, *malloc_p_a);
printf("&global_init_a=%p, global_init_a=%d\n", &global_init_a, global_init_a);
while(1)
;
return 0;
}
编译程序,gcc addr.c –o addr 然后运行程序 ./addr
ps aux 查看进程的进程号
cat /proc/6882(进程号)/maps 查看各段地址, 查看更为详细的地址readelf –S addr
对照着段的信息和程序打印出来的地址即可查到各个变量所在段。
这篇文章讲得也不错。http://www.jianshu.com/p/9f981f6433d1
标签:
原文地址:http://www.cnblogs.com/yuyounglife/p/5661238.html