一、一个C程序的生命周期
一个C程序的生命周期从源文件开始,源文件为文本文件,每个字节表示程序中的某些文本字符。源文件为了能执行,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种被称为“可执行目标程序”的格式打包好,并以二进制文件的形式存放起来。
编译器驱动程序(如GCC)读取源文件hello.c,并把它翻译成可执行的目标文件hello,分为四个阶段完成。执行这四个阶段的程序(预处理器,编译器,汇编器,链接器)共同组成编译系统。如下图所示:
- 预处理阶段:预处理器(cpp)识别以#开头的命令,将头文件的内容替换到程序文本中。得到另一个C程序,通常以.i作为文件扩展名。
- 编译阶段:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含了一个汇编语言程序。
- 汇编阶段:汇编器(as)将hello.s翻译成机器语言指令,并将这些指令打包成一种叫做“可重定位目标程序”的格式,保存为目标文件hello.o。这是一个二进制文件。
- 链接阶段:hello程序调用了printf函数,他是每个C编译器都提供的标准C库中的函数,它存在于一个名为printf.o的单独预编译好了的目标文件中。链接器(ld)负责将其合并到我们的hello.o目标文件中,结果得到了hello文件。它是一个“可执行目标文件”,可以被加载到内存中,由系统执行。
二、运行hello程序
在linux的shell程序中,我们通过"./hello"指令来执行程序。当我们在键盘输入一个指令,shell程序将字符逐一读入寄存器,再把它放到内存中。
当我们在键盘上敲下回车时,shell就知道结束了命令的输入。然后执行一系列指令来加载可执行文件hello。这些指令将hello目标文件中的指令和数据从磁盘复制到主存。
接着,处理器开始执行hello文件中的main函数对应的机器语言指令,这些指令将“hello world\n”字符串中的字节从主存复制到寄存器文件,再从寄存器文件复制到显示设备。
三、高速缓存的重要性
上面的例子中,系统花费大量时间将信息从一个地方复制到另一个地方,这些复制就是开销,减慢了程序真正的工作。因此,系统设计者需要让这些复制操作尽可能快的完成。于是他们采用更小更快的存储设备,称为高速缓存处理器(cache)暂时存储处理器近期可能用到的信息。
存储器层次结构的主要思想是上一层存储器作为低一层存储器的高速缓存。
四,操作系统管理硬件
计算机系统分层示意图
操作系统有两个基本功能:
- 防止硬件被失控的应用程序滥用
- 向应用程序提供简单一致的机制来控制复杂又通常大不相同的硬件设备。
操作系统通过几个基本的抽象概念(进程、虚拟内存和文件)来实现这两个功能。
- 进程:进程是操作系统对一个正在运行程序的抽象。在一个系统中可以同时运行多个程序,每个程序好像都在独占硬件资源。
- 线程:一个进程实际上可以由多个称为“线程”的执行单元组成,每个线程都运行在进程的上下文中,共享同样的代码和全局数据。
- 虚拟内存:这是一个抽象概念,它为每一个进程提供了假象,单独使用主存,称为虚拟地址空间。
- 文件:即字节序列,每个IO设备都可看作文件。