程序的编译过程
为了说明程序的编译过程,我们用经典的hello world程序作为例子
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("hello world!!!\n");
return 0;
}
在linux系统中,我们用GCC编译器将源程序文件helloworld.c编译成可执行目标文件helloworld。
zengwh@zengwh:~/test_code$ gcc helloworld.c -o hello
zengwh@zengwh:~/test_code$ ./hello
hello world!!!
这个过程经过四个阶段,分别是预处理阶段,编译阶段,汇编阶段和链接阶段。执行这个四个阶段的程序分别是预处理器,编译器,汇编器和链接器,一起构成了编译系统。
- 预处理阶段:预处理器(cpp)根据‘#’包含的头文件,将头文件的内容加进源程序中。得到新的程序文件文本,以“.i”为文件扩展名。比如说helloworld程序包含stdio.h头文件,这个阶段将这个头文件的内容插进源程序中
- 编译阶段:编译器(ccl)将文本文件hello.i翻译成汇编程序hello.S。也就是将高级语言翻译成汇编代码,低级的机器语言指令。
- 汇编阶段:汇编器(as)将汇编程序hello.S翻译成机器指令,将这些指令打包成可重定位目标程序的格式,并把结果保存在hello.o文件中,hello.o是个二进制文件。
- 链接阶段:链接器(ld)将会把一个工程中所有的.o文件链接合并成一个可执行目标文件,可以被加载在内存中,由系统运行。
系统硬件组成
高速缓存(Caches)
高速缓存用来存放处理器近期可能需要的信息,用来加快程序在CPU的运行,下图是一个典型系统中的高速缓存存储器。
位于处理器芯片上的L1高速缓存访问速度跟访问寄存器的速度几乎一样快,L2高速缓存通过一条特殊的总线连接到CPU,访问速度比L1慢5倍左右,但比访问主存要快5-10倍。更新的系统还会有L3高速缓存,他们都是用一种SRAM的硬件技术实现的。这样,系统就可以获得很大的一块存储器,而且访问速度也很快。
程序具有访问局部区域里的数据和代码的趋势。通过将可能经常访问的数据保存在高速缓存中的方法,大部分的存储器操作都将在快速的高速缓存中完成,程序性能大大提升。
操作系统管理硬件
在处理器中,指令集结构是对实际处理器硬件的抽象,在操作系统中,文件是对I/O的抽象,虚拟存储器是对程序存储器的抽象,进程是对一个正在运行的程序的抽象,虚拟机则是对整个计算机(包括操作系统,处理器和程序)的抽象
进程
进程是操作系统中对一个正在运行的程序的一个抽象。一个系统可以同时运行多个进程程序,而每个进程都好像独占使用硬件。并发运行则是说一个进程的指令和另一个进程的指令是交错运行的。
线程
一个进程可以由多个线程组成,每个线程运行在进程的上下文中,并共享同样的代码和全局数据。
虚拟存储器
虚拟存储器是一个抽象概念,为每个进程提供一个假象,即每个进程好像在独占使用主存。每个进程看到的是一致的存储器,即是虚拟地址空间。图中的地址是从下往上增大。
- 程序代码和数据:对所有进程来说,代码都是从同一固定地址开始,接着就是全局变量对应的数据位置。代码和数据区一开始运行就已经规定大小。
- 堆:堆可以运行的时候动态地扩展和收缩,比如调用malloc或free函数的时候。
- 共享库:大约在地址中间,用来存放像C标准库或数学库这样共享的代码和数据区域。
- 栈:位于用户虚拟地址空间顶部的是栈,编译器同它来实现函数调用。和堆一样,在程序运行期间可以动态地扩展和收缩。每调用函数,栈增长,函数返回,栈收缩。
-