操作系统:程序的编译、链接、加载、地址绑定

一、基本概念

程序是有代码、数据、进程控制块组成的
1.什么是数据?
数据指程序中的全局变量、静态变量、常量。

2.什么是指令?
除了数据剩下的就都是指令。

int main(){
	int a=10}

注意:a是局部变量,它并不是数据,而是一条指令,指令功能是在函数栈帧上开辟四个字节,并向其中写入指定值。

3.什么是符号?
在程序中,所有数据都会产生符号,而对于代码段只有函数名会产生符号。而且符号的作用域有global和local之分,对于未用static修饰过的全局变量和函数产生的均是global符号,这样的变量和函数可以被其他文件所看见和引用;而使用static修饰过的变量和函数,它们的作用域仅局限于当前文件,不会被其他文件所看见,即其他文件中也无法引用local符号的变量和函数。

二、编译

  1. 预处理
    预处理阶段将以放置在文件中的预处理指令来修改源文件的内容。(如:宏定义指令、条件编译指令、头文件包含指令、特殊符号)

  2. 编译
    通过语法分析和词法分析,再确认所有指令都符合语法规则后,将其翻译为汇编代码

  3. 汇编
    把汇编语言翻译为目标机器语言,即二进制可重定位目标文件

  4. 链接
    将目标文件彼此链接生成可执行的目标文件

linux下四个步骤命令:

gcc -E main.c -o main.i  #预编译,生成main.i文件
gcc -S main.i            #编译,生成main.S文件
gcc -c main.S            #汇编,生成main.o文件
gcc main.o -o main       #链接,生成可执行文件

链接器的作用:符号解析和重定位(确定各个段以及各个段中符号的最终地址)

可重定位文件和可执行文件:
经过汇编后的文件是可重定位文件。它的文件格式与可执行文件很像(对于Linux,都是elf文件格式)。对于可重定位文件,它里面的代码与数据,都是各个文件独立的代码与数据,在一个工程中,会存在多个C文件,每个C文件都会被首先编译生成一个可重定位文件,然后经过链接器将这些可重定位文件进行链接,从而生成最终的可执行文件。

对于可重定位文件:

  • 各个段没有具体的起始地址,只要段大小信息
  • 各个标识符没有实际地址,只有在段中的偏移地址(相对地址)
  • 段和标识符的实际地址都需要链接器具体制定,这也是链接器的主要作用

对于可执行文件:

  • 各个段有自己的起始地址,这些地址就是将来要被加载到内存中的地址,有了起始地址,才能说加载到内存,不然都不知道加载到哪里,何来的执行呢?这就是可执行文件与可重定位文件一个区别
  • 可执行文件中的各个符号,都有了正确的地址,以及符号被引用的地方也正确填上了符号的地址

三、地址绑定

程序在执行前,有很多步骤(编译、加载),地址会有不同表示形式。
源程序中地址通常使用符号表示,编译器通常将这些符号地址绑定到可重定向地址,链接程序或加载程序再将这些可重定向地址绑定到绝对地址。
每次绑定都是从一个地址空间到另一个地址空间的映射。

指令和数据绑定到存储器地址可在下面任意时期进行
1.编译时
如果在编译时就知道进程将在内存中的驻留地址,那么就可以生成绝对代码(absolute code)。
绝对代码: 这段代码被编译成在一个在特定的地址工作的代码,并且只在加载到那个特定的地址时才工作。分支和跳转指令都包含一个固定的精确(绝对)地址。它是一种通常在嵌入式系统中找到的代码类型,因此可以保证一段代码将加载到该特定地址,因为它是唯一加载到该地址的代码

2.加载时
如果在编译时并不知道进程将驻留在内存的什么地方,那么编译器就必须生成可重定位代码(relocatable code)。对于这种情况最后绑定会延迟到加载时才进行。如果开始地址发生变化,只需要重新加载用户代码以引入改变值。

3.执行时
如果进程在执行时可以从一个内存段移到另一个内存段,那么绑定必须延迟到执行时才进行。采用这种方案需要特定的硬件。绝大多数通用计算机操作系统采用这种方法。

四、动态链接和静态链接

1.静态链接
将这些存在多种依赖关系的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接。
由很多目标文件进行链接形成的是静态库,反之静态库也可以简单地看成是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。

优缺点:

  • 一是浪费空间,如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本
  • 一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序
  • 静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快

2.动态链接
将链接推迟到运行时。在程序运行时才将它们链接在一起形成一个完整的程序。

  1. 在二进制映像内,每个库程序的引用都有一小段代码,叫存根, 用来定位适当的内存驻留库程序,或如果该程序不在内存时应如何装入库
  2. 存根首先检查所需子程序是否在内存中,如果不在,就将子程序装入内存
  3. 存根会用子程序地址来替换自己,并开始子程序

优缺点:

  • 每个程序都依赖同一个库,多个程序在执行时共享同一份副本
  • 更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来
  • 影响一点性能

五、动态加载

与动态链接类似,不过是将加载延迟到运行时。
采用动态加载,一个程序只有在调用时才加载。所有程序都以可重定位加载格式保存在磁盘上。主程序被加载到内存,并执行。当调用另一个程序时,调用程序首先检查另一个程序是否已经加载,如果没有,可重定位的链接程序将用来加载所需要的子程序,并更新程序的地址表以反应这一变化。接着控制传递给新加载的子程序。

优点:
不用子程序绝不会被加载,如果大多数代码需要用来处理异常情况,如错误处理,那么这种方法特别有用。对于这种情况,虽然总体上程序比较大,但是所使用的部分可能小很多。
动态加载不需要操作系统提供特别的支持。利用这种方法来设计程序主要是用户的责任。

动态链接和动态加载都是需要时加载到内存。但

  • 动态链接
    程序启动时建立了链接(即只有链接地址),需要时载入内存,由操作系统决定
  • 动态加载
    通过程序的方法来控制加载,由程序员决定

参考

https://segmentfault.com/a/1190000019747350
https://blog.csdn.net/qq_37375427/article/details/84947071
https://blog.csdn.net/kang___xi/article/details/79571137
《操作系统概念》

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值