对于我们写好的程序,最终会生成目标文件,那么目标文件的内部结构是如何呢?
目标文件一般包含编译后的机器指令代码、数据,还有链接是所需要的一些信息,比如符号表、调试信息等,而且一般目标文件会将这些不同的信息按照不同的属性,以“节(section)”也叫“段(segment)”的形式进行存储,这里姑且成为“段”吧,那么常见的段包含什么呢?没错,一般包含“代码段”、“数据段”、bss段。
代码段:存放程序源代码编译后的机器指令,在程序中常见的名字有“.code”或“.data”。
数据段:存放经过初始化的全局变量和局部静态变量。,一般名字为“.data”
bss段:未初始化的全局变量和局部静态变量。
案例如下图:
当然目标文件里还有写其他的东西,比如最上面的文件头,这个主要是一些属性、动态链接入口等等。
这里着重说一下bss,这个东西可能很多程序员都眼熟,但是总是感觉不能稍微深刻理解,起码我是这样的,原因其实还是因为用习惯了IDE的原因,IDE把大部分的编译活儿都干完了,从上面说过,.bss段里存放的是未初始化的全局变量和局部静态变量。我们知道未初始化的变量其实都是0,而且变量也是数据,本来是可以存放在.data段里的,但是因为他们都是0,所以为他们在.data段分配空间而且只存放数据0是没有必要的(所有的优化都是要节俭内存使用),当然程序运行的时候,他们的确是要占用内存空间的,并且可执行文件也必须记录所有未初始化的全局变量和局部静态变量的大小总和,这个就记为.bss段,所以
.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,注意这里不包含普通局部变量,必须是静态局部变量。
.bss段在目标文件和可执行文件中并不会占用文件的空间(都是0),但是它在装载时,是需要占用地址空间的(占坑)
所以我们在学习uboot的时候,有个比较常见的点就是要初始化.bss段,还有.bss_start和.bss_end等。
那么还有个问题,为什么要将程序目标文件分段存储呢?这里面可能有下面几个原因:
(1)分割起来好管理,对于代码段,是只读的,而数据段往往是可读写的,所以分开了,便于实现对代码的保护,只需要在生成目标文件时,固定代码段只可读即可。
(2)对于现代的CPU来说,一般都有强大的缓存机制,缓存在现代的CPU中地位非常重要,所以程序必须尽量提高缓存的命中率(这个之前想不清楚),显然只领取和数据区的分离是有利于提高程序的局部性或者说模块性的,而CPU的缓存一般都被设计成数据缓存和指令缓存,所以分开放,对于缓存的命中率提高是有好处的。
(3)在现代的系统中,一般会同时运行某个程序的多个副本,比如打开多个浏览器界面、打开多个word文档、打开多个qq等等,由于这些程序副本的指令都是一模一样的,所以内存中只需要保存一份该程序的指令部分,因为程序代码都是只读的,同时读是不会有危险的,对于其他的只读数据其实也是一样的,比如说图标、图片等,当然每个副本进程的数据区域是不一样的,他们是进程私有的,经过这么一个机制,会发现,这样的话,特别能够节省内存。
原文:https://blog.csdn.net/u012351051/article/details/81148456