程序员的自我修养-读书笔记

       最近在看程序员的自我修养这本书,初次读,主要讲述程序的编译,装载和链接的过程。目前读了一半,将编译,静态链接以及装载的部分读了一下,对应了该书的1,2,3,4,6,10等章节,由于日常的开发主要在linux上面,因此对于windows的相关部分并没有去读。本次就这部分内容作一次读书的小结。
       我在这里曾经对GCC编译器编译程序的过程做了粗浅的整理。现在看来GCC是一个强大的功能,从大的方面看,它可以编译C,C++,Java等语言所写的程序,也就是封装了多种语言的编译环境。从单个语言的角度来看,如我的上一篇文章所提到的程序的编译过程,GCC封装了预处理器,编译器,汇编器,链接器。而编译器则又分为如下的若干个阶段:
       1,词法分析:分词过程,将各条语句按照有意义的字符进行切分。
       2,语法分析:将上述的单个词条,构建起语法树。
       3,语义分析:给语法树的节点添加数据类型,处理诸如类型不匹配的情况。
       4,中间语言生成:将上述的树状结构进行优化。
       5,目标语言生成:生成汇编语言,然后生成机器码。
       因此编译器其实是封装了语法分析器,此法分析器,语义分析器,中间语言生成器,目标语言生成器等。
       在这里我们可以看到分层的思想或者说是模块化的思想在计算机中无处不再。从较早计算机体系结构包含中央处理器,内存,外存,输入输出设备的结构,到计算机分为底层硬件,中层操作系统,驱动层,应用层,在到TCP/IP的分层模型。都有分层的设计。当然本身的这种分层设计就是为了代码的高内聚,低耦合。说白了,就是每一层干好自己的事,屏蔽其它层的影响。我们可以看到不论在哪一层都有多种的厂商提供多种的硬件,软件或者协议,可以较好兼容不同的上下层。回到我们的编译过程,也就是可以根据需要使用不同的语法分析器,词法分析器等。当然书中也只是对编译的过程做了概括性的阐述,要了解细节的话,可能需要深入学习一下编译原理。
       经过编译生成的目标文件,在linux上面就是.o文件。这个文件是一种elf(executable linkable format)格式文件,由名字可以看出,此时生成的.o文件(linkable)以及后面会生成的.a或者可执行文件(executable)是同一种结构。具体分析elf的文件结构:
       首先程序中的两个要素即数据和代码,或者说是变量和函数。他们都有类型,作用域,生命周期的概念。因此这样来说,就可以分为代码段和数据段,数据又分为全局初始化的数据和全局未初始化的数据以及常量数据(例如printf中的参数),和局部静态数据。因此目标文件中应该存为.text,.data,.bss,.rodata,即分别对应代码区(即函数,由C命令转化为的机器指令区),全局初始化的数据区,未初始化的数据区(包括局部静态未初始化数据),只读数据区。
       那么问题就来了,为什么要单独设置一个.rodata和.bss。首先.rodata区和data区的区别就是,.rodata区域的数据只可读不可写,而.data区的数据可读科写。这就对只读的数据做了保护。.bss区的数据是没有初始值的,由于初始值的数据要实实在在的存在.o文件中,要占用存储空间,没有初始值的数据会在调入内存的过程中初始化为0,不需要存储该值,因此节约了存储空间。只需要记录所需要的存储空间大小即可。
       我们现在知道上述的.data区域存储的只是变量的值,那么变量名存储在哪里呢?.o文件单独为变量名以及函数名划分了一个段,该段叫做符号表(.symtab)。符号表存储的就是所有的变量名以及函数名,还有其对应的值,当然其值就是我们提到的变量或者函数的地址,即在.data或者.text中的偏移。由于字符串的长度变化范围很大,因此直接将变量名或者函数名存在字符串表里面是不合适,因此就使用了字符串表(.strtab)将用到的字符串统一归到这一处,然后哪个地方用到这个字符串,就将该字符串在字符串表中的偏移位置发给他。我们知道所有的段也都是有名称的,因此也有一个段表字符串表来存储段的名称。
       上述提到的符号表中的符号值对应的是该符号的存储地址,如果能够找到该符号,则自然是有值的,如果找不到,则有可能该符号的定义存在于其它的文件中。因为在编译阶段是按照.c文件为基本的编译单元的,即一个.c生成一个.o文件。那么针对这种情况来说我们就需要将没有符号值的符号记录下来,就需要用到重定位表(.rel.text,.rel.data),像代码段和数据段分别有自己对应的重定位表。当然还有段表,来对整个文件中所有的段进行描述,包括段名,段的长度,段相对于整个文件的偏移值,段的权限等等。在加上文件头,整个elf文件最基本和重要的结构就是如此。
       在分析完目标文件的结构之后,接下来就进入了将大量的.o文件进行链接,生成一个.a文件或者可执行文件的过程。那么如何将这些.o文件组合在一起呢。前面提到过编译阶段生成的.o文件和链接阶段生成的可执行文件或者.a文件都是elf结构的文件。因此自然而然的,就是将所有.o文件的.text段合并成为新的.text,将所有的.data段合并生成一个新的.data的段,其它所有的段都是按照同样的道理。
       这里面着重说一下符号表,由于所有.o文件的符号表都要合成一个大的符号表,因此就可能出现不同.o文件中的符号表存在同名的符号,这就是重定义错误。当然不能仅仅只根据符号名,还包括符号的类型,比如说是全局,局部还是common等。common类型主要是针对有强弱符号的情况。
       当然还有我们所说的重定向表,根据重定向表来确定哪些变量和函数需要重定向,如果在符号表中找不到相应的符号重定向地址,则会报变量或者函数未定义错误。
       至此对应的段进行合并之后就形成了.a文件或者可执行文件。
       有了上述文件之后,就到了程序的执行阶段。那么程序是如何的调入内存的呢?在计算机的发展初期阶段,就是将整个文件调入内存执行。但是后来软件越来越大,后面就产生了按需要的调入内存执行,直至今天所采用的就是分页调入。页的概念就长度固定大小的空间。比如页的大小为4096个字节,那么16k的内存就可以分为2个页。因此内存可以被划分成为若干个页,而相应的可执行文件也可以划分成为若干个页。分页调入的基本思想也是,当内存缺页的时候,将elf文件中相应的页调入内存。那么当内存满的时候,哪些页面会被调出呢,这里面就涉及到页面的调度算法,包括FIFO以及LUR等。
       那么elf文件中的各个段和内存的对应关系是啥呢。如果说将每个段对应的映射到内存相应的段,这是一种办法。但是由于elf文件中段的个数众多,按照页的方式进行映射的话,会容易产生内存碎片,也就是不满一个页的段,要单独占用一个页。因此,将段权限相同的段进行合并装载会减少内存的浪费。即将可读可执行的段合并映射,将可读可写的段合并映射,将只读的段合并映射。将文件从硬盘映射到内存,可以看成好似从一个层到另一个层的关系,可以看到,不同层之间对各个段的处理是有所不同的。
       最后提到的就是局部变量,应该说,在程序的编译和链接阶段,局部变量是代码的一部分,只有当程序整整的执行起来之后,内存会分配堆栈等空间供这些变量使用。
       以上就是读了该书一半之后的思考和总结。
       本文为CSDN村中少年原创文章,转载记得加上小尾巴偶,博主链接这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

村中少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值