zoukankan      html  css  js  c++  java
  • 第八次读书笔记

      这一节的内容介绍了编译器做了什么。其中有一个是语法分析,对不同的记号进行分析,加上它们的优先级,形成语法树。这里的树类似于在结对编程我们采用的表达式二叉树,可见树这个数据结构应用之广。而语义分析是对这棵树就行修改和完善的过程,但只能进行静态语义的分析,动态语义的错误要等运行时才会出现。所以我们在编程运行时出现错误,我们是不是可以先思考一下有哪些错误是会是动态语义出现的错误,比如说除以零,指针越界之类的。

      编译器分为编译器前端和编译器后端,前端负责产生与机器无关的中间代码,编译器后端将中间代码转换成目标机器代码,而跨平台的编译器而言,它们针对不同平台使用同一个前端和数个后端,这样就可以节省工作。

      在源代码被编译成了目标代码后,唯一的问题就是变量的地址没有被确定。事实上,定义在其他模块的全局变量和函数在最终运行时的绝对地址在最终链接时才能确定,所以编译器将一个源代码文件编译成一个未链接的目标文件,然后由链接器最终将这些目标文件链接起来形成可执行文件。

      事实上链接这个概念,从还没高级语言就开始了,原因是如果我们一开始就确定各个子程序的物理地址,我们进行程序之间的跳转就需要这些物理地址,那么当我们修改了一行代码,那么所有的物理地址都将被修改,这就叫做地址重定位。这个麻烦事恰恰解释了我们学的微机原理中的用标号来标识地址的道理。

      然而现在一个软件的代码量是很大的,他们往往根据功能或者性质被划分成成千上万个模块,而我们希望对这些模块单独测试或重复使用,所以往往将他们分开编写,将程序块组合的过程涉及模块之间的通信,通信方式便是模块间符号的引用,所以链接是一个拼接过程。

      链接,就是把模块与模块之间的相互引用的部分处理好。而连接器要做的工作就是高级地调整地址。

      编译器在编译程序的时候对不同模块的函数或变量调用的地址先选择搁置,而修改地址这部分工作就是让链接器完成的,这也是静态链接的基本工作。所以我们平时编的程序就用到了静态链接。

      所以大概总结一下,事实上目标文件已经有可执行文件的形式了,只是它的地址有点还没有被调整。

      所以目标文件和可执行文件一样,都是以可执行文件格式储存的。事实上,动态链接库和静态链接库都是按照可执行文件格式储存的。静态链接库稍有不同,相当于很多目标文件的打包。

      目标文件分为几个部分。第一个是文件头,包括了这个文件的各种信息,包括文件属性(是否为可执行文件/静态链接/动态链接/),入口地址,以及操作系统的信息。代码段,为执行语句编译成的机器代码,初始化的全局变量和局部静态变量放在数据段里。这和我们学习的汇编语言很接近,说明汇编更贴近硬件本身,也体现了高级编程语言和低级编程语言的区别。

      有一个段就做.bss段,用来存放未初始化的全局变量和局部变量。

      学汇编的时候就想问一个问题:数据段和代码段为什么要分开放,在这里得到了解答。

      首先是在程序被装载过后,数据区和代码区会被分别映射到两个虚存区域,因为数据是可以被进程读写的,而代码是只读的,我们就可以将这两个区域进行不同的属性设置,防止程序段被修改。

      接下来还有一个重要原因就在,鉴于代码的只读性,其就可以被共享,事实上凡是具有只读性的东西都可以被共享,比如说图标等等。共享的好处在于,如果这个系统同时运行着一个程序许多副本,内存只要保留一份代码段,这是进程之间共享的。当然每个程序的数据段是进程私有的。

      共享指令这一概念在现代的操作系统中占据了极为重要的地位,特别是在有动态链接的系统中节省了大量的内存。

      在链接中,目标文件之间相互拼合实际上是目标文件对地址的引用,即对函数和变量的地址的引用。在链接中,我们将函数和变量统称为符号,函数名或变量名就是符号名。因此链接的一个关键在于符号管理。每一个目标文件都有一个相应的符号表,这个表里记录了目标文件中所用到的所有符号。,每个符号有一个对应的值叫做符号值,这也是变量和函数的地址。符号表中:定义在本目标文件中的全局符号、在本目标文件中引用的全局符号、段名(其值是该段的起始地址)、局部符号(链接器会忽略)、行号信息。在链接过程中,只有前两类的符号是重要的。

      ELF符号表是一个结构体数组,这个结构体数组记录了跟符号有关的信息。

      st_info符号类型和绑定信息,符号类型指明是/未知符号类型/数据对象,变量数组/函数或可执行代码/段/文件名。绑定信息指明局部符号/全局符号/弱引用

      st_shndx符号所在段:定义在本目标文件中:所在段在段表的下标/不定义在本目标文件:文件/COMMON类型的符号/未定义

      st_value符号值:在本目标文件中且不知COMMON类型:偏移量/COMMON块:对齐属性/可执行文件:符号的虚拟地址,与动态链接器有关。

      在使用ld作为链接器来链接产生可执行文件时,链接器会帮我们定义很多特殊的符号。这些符号并没有在自己的程序中定义,但是可以直接申明并且引用,称为特殊符号。

      这些符号被定义在ld链接器的链接脚本中。

      而随着代码文件的增多,符号名重复成为一个特别严重的问题。最初的修改方法是在编译过程中进行修改,也就有了符号修改一说。而在C++中,支持函数重载,此时函数名是一样的,于是C++就引出了符号修饰和符号改编的机制。

      函数签名:函数签名包含了一个函数的信息,包括函数名/参数类型/所在类/命名空间。作用:识别不同的函数。编译器在将C++源码编译成目标文件时会通过函数签名惊醒修饰形成符号名。

      由于C++和C的编译过程不一样,如果我们在C++中引用C的头文件并需要使用里面的函数,则会遇到编译后找不到匹配的符号的问题,这时候有一个代码可以帮助实现;

    #ifdef _cplusplus
    extern "C"{
    #endif
    void *menmset(void*,int,size_t);
    #ifdef _cplusplus
    }
    #endif
    View Code

      这个技巧在系统头文件中里面都会被用到。

     

  • 相关阅读:
    Redis实战——redis主从备份和哨兵模式实践
    Shiro的Subject和Sessoin的创建
    Shiro配置cookie以及共享Session和Session失效问题
    Shiro的校验Session是否过期处理的过程
    Maven父级pom.xml配置文件
    我的Linux之路——虚拟机linux与主机之间的文件传送
    Redis实战——安装问题汇总
    我的Linux之路——实现虚拟机VMware上linux与windows互相复制与粘贴
    Redis实战——phpredis扩展安装
    推荐一个面向对象的javascript框架mootools
  • 原文地址:https://www.cnblogs.com/HelenL/p/8891732.html
Copyright © 2011-2022 走看看