你真的懂'Helloworld'吗?从编译器到OS内核系列:编译器基本概念

https://m.toutiao.com/is/eUMMXMj/

本文是《你真的理解'Hello world'吗?从编译链接到OS内核系列专题》的第一章的第一小节,主要介绍编译器的基本概念以及C语言程序的构建过程。全系列大纲如下:

前言

第1章 编译器的工作原理

1.1 编译器的基本概念及C语言程序的构建过程(本篇)

第2章 链接器的工作原理

第3章 深入分析ELF文件格式

第4章 动态链接库与静态链接库

第5章 程序的数据存储

第6章 函数参数、返回值的传递过程

第7章 虚拟内存

第8章 程序的加载和重定位

第9章 程序在内存中的布局

第10章 带你重新认识main()函数

第11章 从应用程序到系统调用

第12章 程序的退出和资源回收

本系列专题将带你了解'Hello World'背后隐藏的那些极为重要却又鲜为人知的底层技术,让你对计算机系统的基本原理有一个全面深刻的理解。

有兴趣的童鞋,不妨关注一下吧!

注:本篇的重点是介绍基本概念,让童鞋们对C语言程序构建过程有一个整体的了解,后续会有专门的章节详细介绍编译、链接的实现原理等技术细节,敬请期待!

引子

人与人之间可以通过文字语言或者肢体语言进行交流,海豚之间通过发出不同频率的“脉冲声”进行交流,计算机之间则通过由0和1组成的电信号进行交流。

那么,人与计算机之间如何进行交流呢?这就不得不用到编译器了。

编译器是什么

简单来讲,编译器就是把一种语言(通常是某种高度抽象的高级语言)转化为另一种语言(通常是某种低级语言)的计算机程序。

编译器要解决的根本问题

人类无法直观地理解电信号所携带的信息,同样,计算机也无法理解人类使用的自然语言所表达的含义。

因此,要想让计算机能够“听懂”人话,帮人办事儿,就必须要能够把人类的语言,翻译成计算机能够识别的二进制机器指令。而这,就是编译器所要解决的最根本的问题。

C语言程序的构建过程

'Hello world'我们再熟悉不过了,它是怎么从我们编辑器里的文本文件转化成可执行的二进制文件呢?

C语言的构建过程,有下面几个典型的过程:

预编译

所谓预编译,是指在正式编译阶段之前,对源码文件先进行一系列的预处理,以便于后续编译阶段的处理。预编译阶段的主要工作有:

  • 把'#include'指令中引用的头文件展开在当前源文件中。
  • 把'#define'指令定义的宏标记在源文件中引用该标记的地方进行展开。
  • 对'#if'、'#ifdef'、'#elif'、'#else'、'#endif'等条件编译指令进行处理,把条件不满足的代码删除。
  • 删除所有'//'和'/* */'标记的注释信息。
  • 其他处理,如添加行号和文件位置标识等信息,以及处理'#pragma'等预编译指令。

如下图中,左侧是test.h文件,右侧是main.c文件

test.h 和 main.c

请注意,我们在头文件test.h中定义了两个宏,并且声明了area()函数,然后在main.c中包含test.h,并且在main()函数中引用两个宏定义。

我们用下面的命令对它进行预处理:

gcc -E -I. main.c -o main.i

得到的输出文件是main.i,如下图右侧所示:

main.c 和 main.i

从main.i中,我们看到test.h被完全的展开了,并且对宏定义LENGTH和WIDTH的引用也被展开,所有的注释被删除,此外还添加了文件位置信息和行号信息。

预编译器所做的工作比较简单,原理和实现也相对容易,主要就是普通的文本处理。

编译

编译阶段的主要是把经过预处理的C语言源代码经过一词法分析、语法分析、语义分析和代码优化之后,产生汇编代码,也就是在编译阶段的目标代码。

典型的编译过程,如下图所示:

典型的编译过程

我们对main.i文件进行编译:

gcc -S main.i -o main.s

得到的汇编文件main.s的如下图所示:

main.s

这是程序构建过程中的最核心也是最复杂的环节之一,我将会在下篇文章中对其实现原理进行详细讲解。

汇编

编译阶段产生的目标代码是汇编代码,相对原始的C语言文件来说,它的语法更加简洁,数据类型更加简单,程序结构更易处理。

尽管汇编代码跟最终的二进制机器指令已经非常接近,但它仍然属于文本语言,无法直接被计算机识别。

因此,汇编阶段所做的主要工作,就是根据CPU厂商提供的汇编指令和机器指令的对照表,把汇编指令翻译成机器指令,这个阶段的最终输出结果被称为目标文件

对main.s进行汇编,将生成目标文件main.o:

gcc -c main.s -o main

我用file命令看一下main.o的信息:

可以看出,此时main.o的类型是可重定向(relocatable)的目标文件,还不是最终的可执行文件。

我会在稍后更新的本系列专题的第二章《链接器的基本原理》中详细讲解两者的区别。

汇编器原理比较简单,但是由于CPU所支持的指令多而杂,实现起来会比较繁琐。

扩展知识 - 把文本形式的汇编代码翻译成二进制机器指令的程序叫做汇编器,而与之相反的,把二进制机器指令翻译成文本形式的汇编代码的程序,叫做反汇编器。

在程序调试过程中,反汇编器经常会用到,尤其在调试一些系统底层软件的时候,反汇编器是必不可少的工具,常用的调试工具一般都会集成反汇编器,比如gdb,Virtual Studio等。此外,在软件逆向工程中,反汇编器也是最核心的工具之一。

链接

链接是C语言程序构建过程的最后一个环节。简单来说,链接阶段的主要工作,就是把多个目标文件之间建立起来一种联系,然后根据这种联系,把这些相互关联的目标文件组合起来,最终生成一个可执行文件。

这里的目标文件,包括汇编阶段产生的目标文件,以及这些目标文件中引用的外部函数所在的库文件,包括动态链接库和静态链接库。

我们仍以计算长方形面积的程序为例,但是稍稍做些修改,我们把area()函数单独放在一个test.c中,源码如下图所示:

然后重新执行上面的预编译、编译和汇编的步骤:

此时我们得到两个目标文件:test.o和main.o,然后

gcc test.o main.o -o main

这样,我们就把test.o和main.o链接成了一个可执行文件main.

注:为了便于概念的理解,我在这里故意忽略了对外部库的链接处理,我将在本系列专题的第二章《链接器的工作原理》中,重点讲解动态链接和静态链接过程。

小结

编译器要解决的根本任务是要将人类可识别的文本语言转换为计算机可识别的二进制指令。

对于C语言来说,编译器在程序构建过程中,经过预编译、编译、汇编和链接,把C语言编写的源文件最终生成可执行的二进制文件。

后续更新内容预告

本篇作为本系列专题第一章的第一节,主要侧重读概念的介绍和C语言程序构建的基本过程。后续章节将会更注重技术实现的细节。

下篇是本系列专题第一章的第二小节,将重点讲解编译器的实现原理,通过实例讲解词法分析、语法分析、语义分析、中间表示生成、代码优化以及代码生成的技术细节。

然后就是本系列专题的第二章《链接器的工作原理》,实例讲解链接过程中的各种技术细节。

思考题

经过本节的介绍,我们知道了C语言程序构建过程要经过预编译、编译、汇编、链接等过程,不妨思考一下:

  1. 所有的编程语言编写的程序都要经过这样的构建过程吗?不同编程语言编写的程序,构建过程有什么差异吗?
  2. C语言程序是否一定要经过这些构建过程呢?有没有什么办法可以直接运行C语言源码文件呢?

对于这些问题,你有自己的答案吗?欢迎留言讨论!

如果觉得有用,就动动手指点个赞呗:-)

对编译器、OS内核、虚拟化、性能优化等底层技术感兴趣的童鞋,不妨关注下,后续会持续更新相关内容。

(0)

相关推荐

  • 编译与链接

    之前天天编软件,总是一键"build",然后等啊等啊,修改报错的地方.相信也有一些非计算机专业的软件工程师和我一样,从来不知道到底我们编译出来的hex文件到底是怎么生成的.直到最近 ...

  • gcc和g 是什么,有什么区别?

    素材来源:C语言中文网 编辑整理:strongerHuang 目前(2020-09)GCC 编译器已经更新至 10.2版本,其功能也由最初仅能编译 C 语言,扩增至可以编译多种编程语言,其中就包括 C ...

  • 编译型语言和解释型语言的区别

    我们编写的源代码是人类语言,我们自己能够轻松理解:但是对于计算机硬件(CPU),源代码就是天书,根本无法执行,计算机只能识别某些特定的二进制指令,在程序真正运行之前必须将源代码转换成二进制指令. 所谓 ...

  • 乐创DIY C语言讲义​——2.2节

    2.2 文件依赖关系和编译流程 C语言的源文件,包括.c文件和.h文件两种,一般地,我们把实现功能的函数,流程代码放在.c文件中,将一些变量的声明,类型定义的代码放在.h文件里面.在使用时,我们首先需 ...

  • 你真的懂“Hello world”吗?从编译到OS内核系列:编译器实现原理

    本篇是<你真的理解'Hello world'吗? 从编译链接到OS内核系列专题>的第一章的第二小节,重点介绍编译器的实现原理. 本系列专题文章,重点分享一些程序运行背后的一些极其重要却又极 ...

  • 没有看过这10栋楼,不算真的懂巴黎!

    巴黎以其拥有百年历史的奥斯曼建筑而形成独特的"巴黎式街景",不过巴黎也有不少非常不"奥斯曼"的建筑,它们也都一直被好好保存着. 今天便来分享巴黎奇特建筑系列,一 ...

  • 日常养生丨上火,你真的懂吗?

    渐渐地 天气变热,气候干燥 体内水分容易散失 身体总是出现各种各样的问题 口腔溃疡,牙龈肿痛,喉咙发炎,脸上生痘-- 这一系列的身体不适 都是"上火"惹的祸! 现代社会,年轻人经常 ...

  • 你真的懂二极管?稳压,温度补偿,限幅电路这7种应用电路解析

    许多初学者对二极管很"熟悉",提起二极管的特性可以脱口而出它的单向导电特性,说到它在电路中的应用第一反应是整流,对二极管的其他特性和应用了解不多,认识上也认为掌握了二极管的单向导电 ...

  • 《说文解字》第417课:天天“说”话,你真的懂“说”字吗?

    接着我们的<说文解字>课程,本课只讲"言"部的一个汉字"説","説"在现代汉语简化为"说","说& ...

  • 人生最需要读的六句话,总有一天会真的懂的

    不知道,你们在读书的时候,有没有哪一句话触动过.读一本书,总会被某一个句子感动,甚至影响很深.有人因为一句话,会对生活有了新的感悟:也有人会因为一句话,做一些不可思议的决定.书就是这么的独特,独特到会 ...

  • 你真的懂核聚变吗?核聚变的细节和数学原理

    到19世纪末,对太阳光的光谱测量已经揭示出太阳含有大量的氢和少量的氦.科学家们在20世纪的头几十年就充分意识到了这一点,但由于相对论那时才刚刚被发现,而量子物理学也处于早期发展阶段,因此不可能将这种观 ...

  • 你写字那么好看,但是你真的懂书法吗?

    我们在网上经常可以看到一些人评论其他人的书法,无非就是两个论调,第一个:这个人写字写得真好啊,真懂书法! 那么第二种情况就是,你看,这个人书法不怎么样,又在那里胡说八道了,他一点也不懂书法! 因此,在 ...

  • 《指环王》重映:尬吹尬黑的人,你们真的懂什么叫美吗? | 循迹晓讲

    ◎ 循迹 ·用文化给生活另一种可能 ◎ 作者:獵戶座零戰 ◎ 编辑:马戏团长 ◎ 全文约8700字 阅读需要25分钟 ◎ 本文首发于[循迹晓讲]公众号 未经授权 不得转载 随着各地疫情形势的好转,从年 ...