1.获取程序的源代码
2.对源代码进行预处理,生成预处理之后的.i文件,一般使用预处理器cpp工具进行预处理。注意,这里的cpp是C Preprocessor的缩写,并不是C-plus-plus的意思。
预处理主要做了以下事:
①处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。这是一个递归的过程,如果被包含的文件还包含了其他文件,会递归地完成这个过程。
②处理条件预编译指令,比如#if、#ifdef、#elif、#else、#endif。 删除#define,展开所有宏定义。
③添加行号和文件名标识,以便于在编译过程中产生编译错误或者调试时都能够生成行号信息。
3.对.i文件进行编译。一般使用
gcc -S hello.i -o hello.s //-S只激活预处理和编译,就是指把文件编译成为汇编代码。
gcc其实已经做了封装,背后是使用一个名为cc1的工具
通过词法分析、语法分析、语义分析,编译之后生成.s汇编代码
4.对.s汇编代码翻译为.o二进制机器码
用as工具。虽然这个文件已经是二进制的机器码了,但是它仍然不能执行,因为它缺少系统运行所必须的库,比如C语言printf()对应的汇编语言的puts函数。确切的说,系统还不知道puts函数在内存中的具体位置。
5.对.o二进制机器码进行链接
通常使用以下指令完成动态链接:
gcc hello.o -o hello
也可用静态链接
ld -static -o
先来阐述一下DLL(Dynamic linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类
链接分类(链接实际就是将多个段基址合并为一个段基址吗,生成统一基址的逻辑地址)
| 链接方式 | 特点 |
|---|---|
| 静态链接 | 事先进行链接,以后不再拆开的链接方式 |
| 装入时动态链接 | 在编译之前已经明确知道要调用DLL中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含DLL函数的代码;当程序装入时,调用函数的时候利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中(全部函数加载进内存 |
| 运行时动态链接 | 在执行过程中,当发现一个被调用模块尚未装入内存时,立即由OS去找到该模块并将之装入内存,把它链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上 |
链接完成之后就是.out可执行程序了
6.运行程序
装入是指由装入程序将装入模块装入内存,构造PCB,形成进程,开始运行(使用物理地址)。又称重定位。即将逻辑地址转为物理地址并装入具体的内存。
装入方式分类:
| 装入方式 | 特点 |
|---|---|
| 绝对装入方式 (绝对装入方式只适用于单道程序环境) | 在编译时,如果知道程序将驻留在内存的什么位置,那么,编译程序将产生绝对地址的目标代码。 绝对装入程序按照装入模块中的地址,将程序和数据装入内存。 装入模块被装入内存后,由于程序中的逻辑地址与实际内存地址完全相同,故不须对程序和数据的地址进行修改。程序中所使用的绝对地址,既可在编译或汇编时给出,也可由程序员直接赋予。 |
| 静态地址重定位 | 在装入时完成虚拟地址到内存地址映射。地址变换通常是在装入时一次完成的,以后不再改变。 |
| 动态地址重定位 | 不是在程序执行之前而是在程序执行过程中进行地址变换。更确切的说,是把这种地址转换推迟到程序真正要执行时才进行,即在每次访问内存单元前才将要访问的程序或数据地址变换成内存地址。动态重定位可使装入模块不加任何修改而装入内存。为使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持。 |



