深入理解计算机操作系统 在系统上运行程序

程序的生命周期

在书本第一章第二节给出了下图所示的一个hello程序在系统上运行时的不同格式。
在这里插入图片描述
C 语言代码最终成为机器可执行的程序,会像流水线上的产品一样接受各项处理,在第七章第一节给出了下图所示的流程:
在这里插入图片描述

  1. c程序转化成. i 文件,这一步是由预处理命令cpp完成
  2. 将.i 文件的内容经过编译器处理转化为汇编代码
  3. 汇编程序经过汇编器变为对象程序,即可重定位目标文件
  4. 链接器通过将可重定位文件和静态库等处理变成可执行目标文件
  5. 最后可执行文件加载到内存运行。

在书的第三章讲解了汇编代码的部分语法,在书的第7章讲述了可重定位文件变成可执行目标文件的过程,在第8章提出了进程的有关概念,第9章详细阐述了内存分配。后续的内容会在7-9这三章内容里展开,也就是说在这三章的内容里,程序都是以机器语言的形式进行处理。

链接

几个重要的概念

符号: 对应一个函数、一个全局变量或一个静态变量
符号引用:程序中调用函数与变量
符号定义:函数与变量的定义
目标模块:一个字节序列
目标文件:以文件形式存放在磁盘的目标模块集合

静态链接

这一步主要完成两个工作:

  1. 符号解析:将符号引用和符号定义关联起来
  2. 重定位: 将符号定义与一个内存位置关联起来,从而在引用这些符号时可以使得引用指向该内存位置

个人觉得符号解析只是完成了符号定义的工作,真正的引用和定义想联系起来还是要靠内存位置。

包含函数与变量的二进制代码和数据存储在目标文件中,目标文件分为三类:

  1. 可重定位目标文件: 包含二进制代码和文件,可以在编译时与其它重定位目标文件合并起来,创建一个可执行目标文件
  2. 可执行目标文件: 包含二进制代码和文件,可直接复制到内存运行
  3. 共享目标文件: 一种特殊的可重定位目标文件 ,可以在加载或者运行时被动态加载进内存并链接。

可重定位目标文件格式详见书本7.4,这里不再赘述。

符号表

在可重定位目标模块中都有一个符号表,用来存放定义和引用的符号的信息。

  1. 有模块m定义并且能够被其他模块引用的全局符号,对应非静态c函数和全局变量
  2. 由其他模块定义并被模块m引用的全局符号
  3. 只能被模块m定义和引用的局部符号,对应于静态c函数和静态全局变量。

介绍了上述的定义后,一个可重定位目标文件ELF格式如下图。
下图引自小土豆博客https://wdxtub.com/csapp/thin-csapp-4/2016/04/16/

其中.bss与COMMON很像,.bss主要存放未初始化的静态变量,以及初始化为0的全局或静态变量
COMMON存放未初始化的全局变量。
符号表存放符号的位置,符号的大小,符号对应的类型(全局或者局部)。

符号解析

上面的内容只是定义了符号,并没有将符号定义与符号引用相结合起来。
在每个模块中,每个局部符号都只有一个定义,局部变量也会有本地链接器符号。对于全局符号的引用解析要麻烦的多。
麻烦:

  1. 当前的符号不是在当前模块定义的,编译器会生成一个链接器符号,交给链接器处理,如果链接器在输入里没有找到就会返回错误信息并终止。
  2. 有很多重复命名的全局变量。

这时编译器会产生一个强弱符号,函数和已经初始化的全局变量是强符号,未初始化的全局变量是弱符号。链接器根据一些规则来处理强弱符号:

  1. 不允许有多个同名的强符号
  2. 当强符号和弱符号同名时选择强符号
  3. 多个弱符号同名时选择弱符号

整个链接部分第一块到这里结束,我们假设链接器读取一组可重定位目标文件,并把它们链接起来,形成一个输出的可执行文件。

使用静态库链接

将所用通用与相关的目标模块打包称为一个单独的文件,随后在链接过程中只复制需要利用的那一部分模块。静态库以存档的形式存放在磁盘中,用.a后缀标识。

重定位

当链接器完成了符号解析,链接器就知道了它的输入目标模块中的代码节和数据节的确切大小,就可以开始重定位步骤了。重定位主要有以下两步

  1. 重定位节和符号定义 例如,data节被合并为输出可执行目标文件的.data节,内存地址赋予这个新的聚合节
  2. 重定位节中的符号引用 利用可重定位条目使得对每个符号的引用指向正确的内存位置。

重定位条目

代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中,这里再提一嘴,全局变量和静态变量靠内存位置,局部变量靠栈。
具体的格式如下图
在这里插入图片描述
symbol 告知要指向的符号名称
offset 引用的节偏移,比如在0xf处开始了调用sum函数了,0xf就是offset
type 如何修改新的引用,直接还是间接引用
addend 偏移调整

可执行目标文件

链接器的工作完成后,我们得到一个二进制文件,包含加载程序到内存并运行它所需的所有信息。可执行文件的格式如下所示:
在这里插入图片描述
可执行文件的连续的片被加载映射到连续的内存段,程序头部表表示了这种映射关系。

加载可执行目标文件

linux程序通过调用execve函数来调用加载器,加载器可以将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过入口点来运行该程序。这样的一个过程叫做加载。
在程序运行时都会有一个如下图所示的内存映像。内核是操作系统驻留在内存的部分。
在这里插入图片描述

动态链接

动态链接共享库

在上百个进程的运行过程中,利用静态库仍然存在内存资源的极大浪费。共享库是一个目标模块,在运行或者加载时可以加载到任意的内存地址,并和一个在内存中的程序链接起来,这个过程叫做动态链接。

整个动态链接的过程就是得到可执行文件以后,同时链接器复制一些重定位和符号表信息,这样在程序运行时链接器可以解析对动态库的引用。随后在加载时注意到了动态库的信息,动态链接器会重定位动态库中的文本数据到某个内存段,这样动态库的位置就确定了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值