从C程序到计算机组成原理

less than 1 minute read

Published:

从C语言程序出发,理解一个程序如何被计算机所执行,总结计算机组成原理的一些要点。本文同时也标志着折磨笔者长达很久的ICS(Introduction to Computer System)短期内告一段落。

主要参考了UCB的课程:CS61C’ Great Ideas in Computer Architecture

和书籍:Computer organization and design: the hardware/software interface

C Program

C语言中的数据类型,包括整数、浮点数、字符、数组等本质上都可以用0-1比特表示。

C语言的整段程序会被加载到内存中运行,同时分配的内存还用于储存全局变量、函数的局部变量、动态分配的空间等。

C语言中的顺序执行其实主要沿着程序一行行执行即可,而判断和跳转等只需要改变程序执行的位置即可。

对于函数调用,需要为调用的函数在栈(Stack)中分配空间储存其局部变量等,记录跳转前的位置,然后调转到对应的函数,当函数执行结束之后返回(Return)到函数调用前的位置即可。

其中,由于母函数和子函数都会同时改变寄存器(Registers)的值,因此必须制定规范。

将寄存器分为调用者保存和被调用者保存(CalleR Saver和CalleE Saver),简单来说就是规定部分寄存器可以被调用的子函数改变,因此母函数需要保存其值,而对于其他部分寄存器,子函数必须保证在返回母函数前将寄存器恢复原值,因此母函数无需保存这部分寄存器的内容。

CALL

CALL是一段C代码从代码到程序所经历的过程。

使用GCC编译main.c文件时,我们发现文件的后缀经历下面的过程。

  • main.c: C语言代码
  • main.s:汇编语言
  • main.o:目标对象
  • a.out: 程序

其中,经历了四个过程:

  • Complie: 将C代码翻译为汇编语言
  • Assemble:翻译汇编语言,并且加入文件描述,此时文件中使用的是相对地址
  • Link:链接不同的文件,合成一个程序,将不同文件的相对地址转换为绝对地址,使得一个程序可以同时调用多个文件
  • Load:加载程序,可以开始执行

CPU

对于已经被翻译为汇编语言的程序,下面要处理的问题是CPU(Central Processing Uint,中央处理器)如何将其执行的问题。

首先,对于汇编语言,可以将需要用到的操作编成一个指令集(ISA,Instruction Set Architecture),包括各种类型的指令,实现包括计算、数据传输(从内存中读取和储存)、控制(跳转、分支)等,基于上述指令,可以实现任意的程序中计算、循环、判断等功能。

CPU需要解决对上述指令的处理,可以将其分为几个步骤:如先取出指令、将其进行翻译、执行计算操作、访问内存、写内存等。

简单的方法是串行地执行上述步骤,但会导致每一条指令都需要等待上一条指令完全执行结束之后才能被执行。

流水线技术是对其很好的加速,通过拆分为几个单元分别执行对应的步骤,每个单元可以执行不同指令。

流水线技术存在的问题是,有些指令需要依赖于其他指令执行结束,比如上一条指令对某个内存进行了改写,下一条指令又需要读写相同的内存位置,称之为冒险,而流水线技术必须处理诸如此类的冒险问题。

Cache

缓存(cache)是CPU和内存之间的部分,可以储存部分内存的复制。

缓存基于程序的空间局部性和时间局部性原理,也即一个程序通常会访问一段连续的内存(例如数组),且在一段时间通常会反复访问同一个内存位置(例如循环),因此可以将内存的一些复制放到Cache中,每次先在Cache中查找,加快了速度。

Cache的关键在于如何选择对内存的复制方案(Mapping),以及当缓存不命中的时候,如何选择策略改变Cache中储存的内容等。

Cache的思想至关重要,在计算机组成的各个部分都经常出现。

Virtual Memory

上述我们都假设每次只有一个程序在运行。

但实际上在真正的计算机使用过程中,有多个程序同时在进行。

但每个程序都认为自己独占了所有的内存,这背后其实是虚拟内存技术。

同时,实际上每个程序占用的内存可能是不连续的,但是虚拟内存技术使得程序认为其在访问连续的内存。

本质上,虚拟内存技术就是将内存的物理地址映射为一个虚拟地址,而每个程序都储存其自身的映射表。

为了避免碎片化问题,将整个内存分为很多大小相同的页表(Pages),每个程序经过一个Page Table储存虚拟页表到物理页表之间的映射关系。

但实际上,一个程序通常不需要同时使用所有的页表,不需要将所有的页表加载到内存中,此时可以通过分级页表技术(Hierarchical Pages),如果下一级页表之下所有的页面都不需要被使用,则这一部分可以不被加载。

页表的管理,同时使得虚拟内存的空间可以大于物理内存的空间,只需要及时进行内存和磁盘的页表交换,这使得一个程序可以运行比计算机的物理内存更大的内存空间。也可以用缓存的思想看待,将内存看作是磁盘的一个缓存,程序可以使用磁盘的空间。

在分页技术中,同样可以使用类似缓存(Cache)的思想。由于虚拟地址到物理地址的翻译需要一定时间,对于经常需要的翻译,可以将其记录在TLB(Translation Lookaside Buffer)中。