从代码到进程
本文主要是简单梳理一下平时写代码,从高级语言到最终加载到内存中的进程,经历了怎样的流程。
流程
以C语言作为开发语言,从编写高级语言的代码开始,到最终的执行经历了以下的流程
整体来看,从c源文件到最后的可执行文件一共有预处理,编译,汇编,链接四个阶段。
预处理
预处理是由预处理器(一般编译器内置)将原始的源代码根据条件编译(#if/#ifdef/#ifndef/#else/#elif/#endif)、宏定义(#define)、源文件包含(#include)、行控制(#line)、错误指令(#error)、和实现相关的杂注(#pragma,多用于规定编译器行为等)以及单独的空指令(#)进行相应的替换、修改等操作,得到预处理后的源代码。单独进行预处理后得到.i格式的文本文件。
编译
由编译器进行,通过语法分析和语义分析将高级语言(这里指C语言)的源代码翻译成低级语言汇编语言的代码,编译器根据一些编译器配置进行编译和优化,最终产生.s的汇编语言代码。
汇编
由汇编器将汇编语言代码翻译成二进制机器码,最终产生的是.o后缀的二进制文件。但是此时这个二进制文件只是包含了部分的代码段和数据段,并不能直接执行。
链接
如下图所示,经过连接器,将目标文件和一些库进行链接,产生新的库或可执行程序。
静态链接
链接器(linker)以拷贝的形式,将外部函数的代码添加到可执行文件(或新生成的库)中,就是静态链接,静态链接的库会在最终形成可执行程序前将外部引用的库中的指令拷贝到代码段中。Linux下的静态链接库以.a为后缀,Windows下为.lib,Mac下为.a
动态链接
动态链接的库不会进行拷贝,而是会在程序加载到内存中作为进程运行时动态加载到内存中,优点是缩小可执行程序的占用空间,并且可以共享库,但在运行环境中必须包含需要的动态链接库。Linux环境下动态链接库多以.so为后缀,Windows平台下则为.dll,Mac平台是.dylib
Linux下的构建工具
GNU Autotools
在大型项目的构建中,首先由维护者使用GNU Autotools生成项目的配置和构建脚本。
autoconf
自动化的安装流程中,autotools中的autoconf根据configure.ac生成configure脚本。configure脚本配置编译参数(如–prefix配置目标路径),生成相关的config.h头文件,并根据运行环境生成对应平台的Makefile。
automake
根据开发者编写的Makefile.am脚本生成对应的Makefile.in模板,用于autoconf生成configure脚本。
Cmake
在项目的构建中,我们也常常使用Cmake编写多级的CMakeLists.txt,进而自动化生成makefile进行构建。
GCC编译器
GCC(GNU Compiler Collection)编译器是Linux环境下最常用的编译器,也常用于程序的编译流程,具体的指令在文章的第一个图示中进行了介绍。