第1章 温故而知新
1.1 从Hello World说起
Hello World程序是计算机编程的经典,如下:
#include <stdio.h>
int main()
{
printf(“Hello World\n”);
return0;
}
问题来了,你深入思考过Hello World程序内部是怎么运行的么?
l 程序为什么要被编译了之后才可以运行?
答:以Hello World程序为例,它是用高级语言编写的,对于计算机而言,它并不认识。所以程序必须要被编译成计算机认识的机器语言才可以运行。
l 编译器在把C语言程序转换成可以执行的机器码的过程中做了什么,怎么做的?
答:我只知道编译过程中会涉及到预编译、编译、装载和链接。
预编译:将文件头(#include<>)和宏等包含进来。
编译:涉及到编译原理,包括词法分析和语法分析,得到目标文件。
装载:将库文件装载。
链接:将目标文件和库文件链接,得到可执行文件。
l 最后编译出来的可执行文件里面是什么?除了机器码还有什么?它们怎么存放的,怎么组织的?
答:不清楚,可执行文件里应该有机器码,具体的得看过书再写。
l #include <stdio.h>是什么意思?把stdio.h包含进来意味着什么?C语言库又是什么?它怎么实现的?
答:#include <stdio.h>指把stdio.h文件的内容包含进来,在预编译阶段,编译器会将stdio.h文件的内容读进来。C语言库是一堆写好的类似stdio.h的文件库,怎么实现不清楚。
l 不同的编译器(MicrosoftVC、GCC)和不同的硬件平台(x86、SPARC、MIPS、ARM),以及不同的操作系统(Windows、Linux、UNIX、Solaris),最终编译出来的结果一样吗?为什么?
答:不一样,对于不同的操作系统,提供的系统API就不一样,例如Windows和Linux提供的系统编程API就不同。对于不同的硬件平台,硬件接口也不一样,不同的编译器内部实现的机理也不同,最终编译的结果会有不同。
l Hello World程序是怎么运行起来的?操作系统是怎么装载它的?它从哪儿开始执行,到哪儿结束?main函数之前发生了什么?main函数结束之后又发生了什么?
答:Hello World程序经过预编译、编译、装载和链接,编程可执行文件,操作系统调用它即可运行。函数从main处开始执行,到return 0;处结束。执行程序时,程序会想操作系统发出中断请求,请求调用CPU,操作系统响应中断,保存相关信息,转而执行程序,程序从main开始执行,执行到return 0;后,程序运行完毕,返回给操作系统。
l 如果没有操作系统,HelloWorld可以运行吗?如果要在一台没有操作系统的机器上运行Hello World需要什么?应该怎么实现?
答:可以运行。需要编译器,具体实现得看书。
l printf是怎么实现的?它为什么可以有不定数量的参数?为什么它能够在中断上输出字符串?
答:printf具体实现肯定跟磁盘打交道了,后续问题我得看书。
l Hello World程序在运行时,它在内存中是什么样子的?
答:不清楚。
1.2 万变不离其宗
计算机概念广泛,达到超级计算机,小到手机上的嵌入式芯片,都可以被称为计算机。只要我们能够深刻地理解x86平台下的系统软件背后的机理,那么其他的平台都是大同小异的,很轻松地就可以举一反三。
对于系统程序开发者来说,计算机有三个部件最为关键:中央处理器CPU、内存和I/O控制芯片,这三个部件几乎就是计算机的核心。
计算机:总线为中心àCPU为中心,单核à多核。
1.3 站得高,望得远
计算机系统软件体系结构采用一种层的结构,有人说过一句名言:
“计算机科学领域的任何一个问题都可以通过增加一个间接的中间层来解决”(原文:Any Problem in computer science can besolved by another layer of indirection.)
计算机整个体系结构从上到下都是按照严格的层次结构设计的。
每个层次之间都要相互通信,层次之间通信的协议我们称之为接口。在层次体系中,接口是被精心设计过的,尽量保持稳定不变,理论上层次之间只要遵循这个接口,任何一个层都可以被修改或被替换。
例:从这个层次结构看,开发工具和应用程序属于同一个层次,因为它们都使用操作系统应用程序编程接口;运行库使用操作系统提供的系统调用接口,以软件中断方式提供;操作系统内核调用硬件规格。
1.4 操作系统做什么
提供抽象接口+管理硬件资源