最近开始在看《深入理解计算机系统》,因为虽然每天在写代码,但实在有太多不明白的东西,很想从底层了解,写完的代码到底经历了怎样一段旅程,最终达到了页面上的效果,否则总觉得自己在闭着眼睛写代码,眼不盲心却盲,这种感觉实在痛苦,就让我从这本书中一探究竟。
#include
int main()
{
printf("hello, world\n");
}
Hello World是我们接触到的第一个程序,我们首先
编辑出来这段代码,生成一个文本文件hello.c存储在硬盘上,叫做源文件。
接着要做的是,生成一个可执行的hello文件,叫做目标文件。因为我们使用的高级语言来编写代码,机器是看不懂的,需要先把高级语言翻译成机器能看懂的二进制语言;在Unix系统中,从源文件到目标文件的转化,是通过编译器的驱动程序来完成的,执行以下命令:
unix> gcc -o hello hello.c
命令执行过程中,一共经历了四步,最终生成可执行的目标文件hello,这四个步骤分别是:预处理,编译,汇编,链接。
具体过程如下图,截取自原书:
结合程序进行说明:
预处理:预处理器(cpp);预处理就是把程序中引用文件,插入到当前文本文件中,比如开头的#include
编译:编译器(ccl);编译这个阶段就比较重要了,我们平常使用的高级语言有很多种,C,C++,Java……,每种语言书写表达的方式不尽相同,如何把这些不同的语言统一转换成一种格式,让计算机理解他们要做的都是同一件事情呢?那就要靠编译器了!这里不同的语言使用的编译器也不相同,但要实现的功能是一致的。编译器中存在一个叫做“汇编语言程序”的东西,汇编语言大概是最接近机器语言的了,编译器要做的事情,就是把预处理完毕的hello.i文件翻译成汇编语言,生成的文件叫做hello.s;
汇编:汇编器(as)要做的呢,就是把编译生成的汇编语言,翻译成机器能读懂的二进制语言,叫做hello.o;
链接:链接器(ld)做了什么呢,看上图中代码调用了一个printf()函数,这是C编译器中的标准C库中的一个函数,单独保存在一个已经预编译好的printf.o文件中,链接器要做的就是把printf.o文件合并到我们的hello.o文件中,最终生成了可执行文件hello;
经历了这么一长串的东西,好像都还没到重点,所谓程序到底在计算机内部经历了什么,好像都还没说到呢~~~所谓盖高楼,一定要打好地基,想要深刻了解一样东西的原理,就必须要先了解与之相关的基础知识~~~
现在再来看看计算机的简单结构组成,这个就一目了然了,经常接触电脑的人应该都了解计算机的基本构成,cpu,主存储器(应该就是内存),磁盘,输入输出设备,他们用一个叫做总线的东西连接起来,互相传输数据。
这里只简单介绍一下CUP和主存:
CPU:cup就是中央处理单元,也是我们计算机的核心部分,就像是一个人的大脑部分一样,人没有大脑就无法思考,计算机没有cpu就无法运算。所谓程序,究其根本,其实解决的还是数学问题,cup要做的也就是加减乘除、与、或、非、异或的数学和逻辑运算。cpu是怎么样来进行这小学生的算术题运算呢?主要依赖的是cpu里面一个叫做ALU(arithmetic and logic unit,算术逻辑单元)的模块,里面大概就是一些门电路的器件,具体原理就要重读大学数电模电这本书了。ALU是个很聪明的小学生,很快完成了一道题的计算,怎么样获得下一道题呢,这就用到了另一个叫做PC(program counter,程序计数器),这里可不是个人电脑的那个pc。程序计数器PC指向了内存中某条指令的地址,cpu从pc指向的内存地址获取一条指令,指令执行完毕后,更新pc,使其指向下一条指令,当然这个指令的位置不一定跟上一条相邻。而寄存器用来存放一些指令、数据、地址等信息。cpu中还包括高速缓存,主要也是用来存放从内存中读取的一些数据等。
主存:主存就是一个临时处理设备,用来存放程序和程序的设备,就是一个以字节为单位的大数组。
好了,看完计算机的结构,我们马上就来说hello world;经过编译器一系列处理,最终生成的可执行目标文件hello,现在就储存在机器的硬盘上,也就是上图的磁盘位置。紧接着,我们要执行另一条命令,去调用这个可执行文件。
unix> ./hello
这个过程发生了什么呢?
首先,我们从键盘上输入./hello到shell命令窗口,书中把这个输入命令的窗口程序称为”外壳“,外壳是一个命令行解释器;输入每个字符都被逐一读取到CPU的寄存器中,然后被保存到存储器中,当我们敲回车时,证明命令结束,此时外壳就回去加载命令中指定的hello可执行目标文件,将文件的内容读取到主存储器中,此时代码的位置转移到主存中。
然后,等程序加载完毕,CPU处理器就开始发挥作用,执行hello中的main程序中的机器指令。将要输出的 "hello world\n" 字符串从主存读取到CUP的寄存器中;
最后,再把寄存器中的hello world字符串,复制到显示设备中,最终显示到屏幕上;
好啦,这就是我们写的第一个程序的奇妙旅程~~~~