计算机体系结构基础


现代计算机都是 Von Neumann Architecture (冯诺依曼体系结构),不管是嵌入式、还是PC机,其主要特点为,
主要部分是CPU(中央处理单元,Central Processing Unit)和 内存(Memory)是计算机的主要组成成分,
内存中存储着数据和指令,CPU从内存中取指令执行,有些指令让CPU进行运算,有些进行读取操作。

1.内存与地址

内存与邮箱类似,每个邮箱 都有自己的编号,而每一个内存单元都有一个地址,内存地址是从0开始的编号的整数,CPU通过地址找到相应的存储单元,并读取数据或指令,与邮箱不同的是一个内存单元所存储的数据是有限的,每个内存单元只能存一个字节的数据,所以通常会使用多个连续的内存单元,例如:int , flaot 等类型占据多个字节,这是就把收个字节作为这个数据的地址。

2.CPU

CPU总是周而复始的执行:从内存取指令,之后执行,然后取下一条指令,再解释执行。其主要组成包括:

  • 寄存器 (Register),是CPU内部的告诉内部存储器,像内存一样可以存储数据,但访问速度会快的多,寄存器种类很多,有些只能用于有特殊的用途,称为特殊寄存器(Special-purpose Register),而另外一些寄存器可以用于运算和读写内存应用中,称为通用寄存器(General-purpose Register)。
  • 程序计数器(PC,Program Counter),是一种特殊寄存器,用于保存CPU所要执行的下一条指令,CPU会通过这个地址,去内存中读取指令并执行,同时程序计数器在保存地址的同时,会保存这条指令的长度,指向内存中下一条指令。
  • 指令译码器(Instruction Decoder)。在通过程序计数器取得指令之后,由于其指令成分还未解析,所以还不能直接执行,而是由指令译码器对指令进行处理,最后再执行。
  • 算术逻辑单元(ALU,Arithmetic and Logic Unit)。如果译码器将一条指令解释为运算指令,就会调动算术逻辑单元去做运算,指令同时会指示结果存到哪里。
  • 地址总线和数据总线(Bus),CPU和内存之间由地址总线、数据总线和控制线连接起来,每条线有1和0两种状态。

如果在执行指令过程中需要访问内存,比如从内存中读取一个数,可以视为如下过程:
在这里插入图片描述

  1. 寄存器将对接到数据总线上,每一位对接一条数据线,等待接收数据。
  2. CPU通过控制总线发送一个读请求,并将内存地址通过地址线发送给内存。
  3. 内存接收到读请求和地址之后,将地址对应的内存位置,按位一一对应到数据总线的另一端,每一位按0或1的状态传递给寄存器,实现数据传输。

向内存里写数据和这个过程类似,只不过数据线的方向相反。


上图中画了32条数据总线连接到寄存器,寄存器位数也是32位,可以说这种体系结构是32位的,比如x86就是这种体系结构,目前主流的是32位和64位。
一般来说寄存器的位数和地址线以及数据线的位数是一致的,从上图可以看出寄存器和数据线位数是一致的,同时由程序计数器这种特殊寄存器的功能(保存内存的一个地址)可以看出其位数适合地址总线一致的。
处理器的位数也称为字长,如果处理器位数是32位那么字长位32,此时16则称为半字,一个字就是32位,同理如果处理器位数是64位,那么字长就是64。
32位计算机有32条地址线,地址空间从0x0000 0000 到 0x ffff ffff ,共4GB,而64位计算机有更大的空间。 不过这里指的总线是内总线,即CPU内部的总线,是直接和执行单元连接的,内总线经过MMU和总线接口转换之后引出芯片引脚的才是外总线,而且外地址线和外数据线的位数都可能不同,例如32位处理器的外地址总线可寻址的空间可以大于4GB。


下面我们看一下取指执行过程:
在这里插入图片描述

  1. CPU从eip寄存器指向的地址中取得一条字长为5的指令,eip并指向下一条指令的地址 80483a7。
  2. CPU对获得的指令进行译码,得知要从地址为 804a01c 开始取四个字节的保存到 eas 寄存器。
  3. 执行指令,取得数字 3 保存到 eax 中。这里要注意读取数字时,不是按地址由低到高读为 0x 0300 0000,而是由高到低读为 0x 0000 0003。对于多字节整形来说,低地址存储的时数字的低位,这称为小端(Little Endian)字节序(Byte Order)。x86平台是小端字节序的,而另外一种顺序,低位保存数字高位的称为大端字节序。
  4. 之后这个指令完成,继续从eip中获得下一个指令的地址,去一个三个字节的指令,然后程序计数器指向80483aa。
  5. 之后对指令进行译码。
  6. 执行指令。
  7. 结束之后,继续从eip中获取指令地址,如此下去。。。

3.设备

在这里插入图片描述
有些设备和内存芯片一样直接连接到数据总线和地址总线上,也正是因为数据线和地址线上要挂在多种设备,所以才叫总线,不过不同的设备占据底质的不同范围。

访问这种设备就像访问内存一样,直接按地址读写即可,不过不一定是单纯的像使用内存一样存储数据和读取数据,有可能是向设备发送指令,数据不一定要保存,还可以是获取设备当前状态,而获取的状态的数据在设备中很可能并不保存。设备中这种可供读写的寄存器叫设备寄存器(与CPU的寄存器不是一回事),而操作这些寄存器的过程就是使用设备的过程,比如像串口发送寄存器里写的数据,串口设备就会发送出去,读取串口寄存器的数据,就可以得到串口设备接收到的数据。

不过还有一些设备集成在处理芯片中。在图中可以看到,从CPU核中引出的总线有一部分连接到处理器的引脚直接引出了,还有一部分没有引出的接到了处理器内部设备上,不论是引出到外部的总线还是接到内部设备的总线都有其相应地址,都可以像访问内存一样访问,很多体系架构(比如ARM)采用这种方式操作设备,称为内存映射I/O(Memory-mapped I/O)。但是x86架构比较特殊,对于设备有独立的端口地址,同时CPU会有独立的端口地址空间,而且CPU会引出额外的地址线来连接片内设备(和访问内存的地址线不同),访问设备寄存器是使用特殊的 in / out 指令,和访问内存的指令也是不同的,这种方式称为端口I/O(Port I/O)。

从CPU的角度来看,访问设备不是像访问内存一样访问,或是用特殊的指令访问,但是实际上使用设备是很复杂的,要考虑许多问题,主要是不同的设备的要求不同,有些要求响应快,有的要求带宽,有些要求热插拔,于是出现了各种适应不同设备的总线,比如USB、1394、SATA、PCI、APG等等,这些设备总线并不是和CPU相连,CPU通过端口I/O或是内存映射I/O连接总线控制器,通过总线控制器去访问挂在总线上的设备。所以上图中设备的框有可能不是设备而是总线控制器。

在x86平台上,硬盘是挂在IDE、SATA或SCSI总线的设备,保存在硬盘上的程序是不能被CPU取指令执行的,而需要拷贝到内存中,这样才能取指令执行,这个过程就叫做加载(Load)。程序加载到内存之后,就成为了操作系统调度执行的一个任务,就称为进程(Process)。一个程序可以多次加载到内存中,成为多个同时进行的多个进程,例如可以开多个终端窗口,每个窗口都可运行一个shell进程,而他们对应的都是 /bin/bash/ 文件。

操作系统本身也是保存在硬盘上的一段程序,计算机在启动时运行一段固定的启动代码(称为Bootloader)先把操作系统加载到内存,然后执行操作系统将程序加载到内存中。
和其他程序不同的是:操作系统常驻内存,其他程序不一定,用户需要哪个程序就把它加载到内存中,那个程序不再需要就会把他终止掉,释放它的内存,像这种管理进程调度、管理内存分配使用和管理各种设备的程序就叫内核(Kernel)。广义上的操作系统还包括一些必不可少的程序,比如Shell是Linux必不可少的。

访问设备和有一个地方是和访问内存不同的,内存只是保存数据而不会产生新的数据,如果CPU不去读他,他也不会主动给CPU数据,所以说内存是被动的读或写。而设备不同在于,设备随时产生新的数据,需要主动传给CPU去处理,例如敲击键盘产生一个输入字符,这就需要键盘产生一个响应给CPU,让CPU做相应处理。这一过程是由中断机制实现的,每个设备都会有一条中断线,通过中断线给CPU发送一个中断信号,CPU正在执行的指令将会被打断,程序计数器会指向某个固定的地址(这个地址有体系结构定义),于是CPU从这个地址开始取指令,执行中断程序(ISR,Interrupt Service Routine),完成中断程序之后继续返回之前位置继续执行后续指令。
比如某种体系结构定义 0x 0000 0010 为中断时跳转的地址,那么就要把ISR实现加载到这个地址,这段代码首先判断的是那个设备引发的中断,然后调用该设备中的中断函数进行进一步处理。
由于各种设备的操作方法不同,每种设备都要使用专门的设备驱动(Device Driver),一个操作系统为了支持广大的设备,就要写许多不同的驱动程序,事实上Linux内核中,绝大部分是驱动程序。设备驱动程序通常是内核中的一组函数,通过读写设备寄存器的读写实现对设备初始化、读、写等操作,有些设备还要写一些中断函数供ISR调用。


4. MMU

现代操作系统普遍采用虚拟内存管理( Vital Memory Management ),这需要MMU(Memory Management Unit,内存管理单元)提供支持,下面简要介绍MMU作用。

物理地址

在这里插入图片描述
首先介绍两个概念,虚拟地址和物理地址。如果没有启用MMU或没有MMU,CPU发出的地址直接由内存芯片(以下称为物理内存,以便于虚拟内存区分开)接收,这种就是物理地址(Physical Address,以下简称 PA )

虚拟地址

在这里插入图片描述
如果启动了MMU,那么从CPU执行单元传出的地址就会被MMU截获,从CPU到MMU的地址称为虚拟地址(Vital Address,以下简称VA),而这个地址将会被MMU翻译成另一个地址被发送到芯片外部的地址引脚上,也就将VA映射成PA。


如果是32位处理器,则内地址总线是32位的,与CPU执行单元相连,而经MMU转换之后的外地址总线就不一定是32位。也就是说虚拟地址空间和物理地址空间是独立的,32位的虚拟空间是4GB而物理空间可能不是有可能大也有可能小。
MMU将VA映射到PA上是以页为单位的,32位的页尺寸通常是4kb。例如,MMU可以将 0x b000 1000 ~ 0x b000 1fff 映射到PA的 0x 2000 ~ 0x 2fff,将0x b000 1008 映射为 0x 2008。物理内存称为页帧或物理页面,虚拟内存映射到物理内存的页是用页表来描述的,而页表存储在物理内存中,MMU会查找页表来获取相应的映射地址。

操作系统和MMU是这样配合的:

  1. 操作系统在初始化或分配、释放内存时会执行一些指令填写页表,并发送指令设置MMU告诉MMU页表在物理内存中什么位置。
  2. 设置好之后,CPU每次使用地址就会经MMU做查表和地址转换,而地址转换会有硬件完成,不需要CPU发送指令控制MMU完成。

我们在程序中所写的函数和变量被编译之后都会变成地址,这是指令中的地址,而这种地址在运行时都是由CPU发出的,会经过MMU进行查表和地址转换由VA转换为PA,也就是说程序中的地址都是PA。


MMU除了提供地址转换以外,还提供内存保护机制。各个体系结构都有用户模式和特权模式之分,操作系统可以在每个页表之间设置相应的访问权限,分为可以访问不可访问,有些只有在特权模式下才可以访问,而访问又分为可读、可写、可执行三种。这样设置好之后,当CPU要对某一地址进行操作时,MMU会检测当前是否具有相应权限,如果没有则会产生一个异常(Exception)。异常的处理过程和中断类似,不同的是异常是由CPU内部产生的,中断产生的原因和CPU当前执行的命令无关,而异常则是由于当前执行的指令出了问题,例如当前指令访问权限不够,除数为0等都会产生异常。

通常操作系统把内存空间分为用户空间和内核空间,例如x86下的Linux系统虚拟地址空间是0x 0000 0000 到 0x ffff ffff,前3GB是用户空间,剩下的1GB是内核空间。用户将内核程序加载到内存空间,在用户模式下不能跳转到内核空间进行执行,也不能访问内核中的数据。这样可以保护内核,如果进程访问了非法地址,顶多是一个进程崩溃,而不会影响到内核的稳定和其他的进程。CPU在产生异常或中断时会切换到特权模式,进而进入内核中的中断或异常服务程序,处理完中断或异常之后切换回用户模式继续执行应用程序。
事实上整个内核就是由各种异常和中断处理程序组成的。

在这里插入图片描述
总结一下:在正常情况下,处理器实在用户模式下执行用户程序的,当遇到中断或异常时会切换至特权模式执行内核程序,处理完成中断或异常操作后,切换回用户模式继续执行用户程序。

段错误是这样产生的:

  1. VA的由MMU检验为无权访问,产生异常
  2. 切换为特权模式,进入内核对异常进行处理。
  3. 内核把这个异常解释为错误,杀死进程。

5.Memory Hierarchy

上述大都是CPU直接或间接访问不同类型存储器去存取数据,那么为什么需要这么多不同类型的存储器呢?

由于硬件技术的限制,我们可以做出存储空间很大但是访问速度很慢的存储器,也能够造出存储空间很小不过访问速度极高的存储器,但不可能即快又大,所以各种存储器各有优劣,这时候我们就要给他们分一下级,这就是 Memory Hierarchy . 按照距离CPU由近到远分别为,寄存器、Cache、内存、硬盘,越靠近CPU访问速度越快,相应容量越小。下图给出了各级的相应参数的典型值。
在这里插入图片描述

Memory Hierarchy
  1. CPU寄存器,位于CPU执行单元中,每个CPU一共就几个或是几十个,而寄存器的大小和处理器的字长相关,所以说寄存器的大小也就在即使字节到几百字节之间。
    半导体工艺:寄存器的名字就是一种数字电路的名字,它由一组触发器组成,每个触发器可以存储一个位的数据,并进行相应的位操作,计算机掉电时数据消失。
    如何访问:访问那个寄存器,如何使用都是由指令决定的。
  2. Cache,和MMU一样位于CPU核中,Cache一般分为几级,比较典型的是图中的两级Cache,一级Cache比较靠近CPU执行单元,二级Cache更靠近物理内存,通常一级Cache有几十到几百KB,二级是几百KB到几MB。
    半导体工艺:Cache和内存都是由RAM(随机存储器)组成的,可以根据地址随机访问,计算机掉电RAM中数据就会丢失。而二者不同的是Cache是由SRAM组成,内存则是由DRAM组成,DRAM电路要比SRAM简单,存储容量也可以更大,不过速度要比SRAM慢。
    如何访问:Cache会存储最近访问的数据,同时Cache的访问速度要比内存快几十倍,所以有效利用Cache可以大大提高计算机的整体性能。
    一级Cache是这样工作的,先使用VA到Cache中查找,找到的话如果进行读操作,就直接把数据存到CPU寄存器中,写操作的话就直接写道Cache中,如果没有找到,就是用PA到内存中去查找,找到之后,不仅仅是把相应数据存储到Cache中,而是把对应这数据附近的一段数据都存到Cache中,这个叫做Cache line典型的Cache line 占据32到256字节。如果计算机还配备了二级缓存,则先使用PA到二级缓存中去查找。一级缓存是用VA寻址的,二级缓存是用PA寻址的,这是他们的区别。
    Cache所做的工作是由硬件完成的,而不像寄存器由指令决定先做什么后做什么。
  3. 内存:位于CPU外的芯片,由地址和数据总线连接CPU。典型容量几百MB到几GB,由DRAM组成。
    如何访问:内存式通过地址来访问的,在开启MMU的情况下,程序指令中的地址是VA,而访问内存用的是PA,通过操作系统来维护PA和VA的映射关系。
  4. 硬盘:位于设备总线上,并不与CPU直接相连,CPU通过总线控制器来访问硬盘。典型容量从几百GB到几TB。
    半导体工艺:磁盘设备是由磁性介质和磁头组成的,访问硬盘时存在机械运动,磁头要移动,磁盘要旋转,由于访问时机械运动的速度很难达到电子的水平,因此访问速度很受限。掉电时数据不会丢失。
    如何访问:有驱动程序操作设备总线去访问,由于硬盘访问时间较长,所以一次访问操作系统就会读几个页面缓存到内存中来,如果后来程序几次都访问到了这几个页面,那么这一次都硬盘的时间就会分摊给程序的多次访问了。

总结如下:

  • 寄存器,内存,Cache都会由于掉电丢失数据,这称为易失性存储器(Volatile Memory),与之对应硬盘时非易失性存储器(Non-volatile Memory)。
  • 除了访问寄存器时是由CPU指令控制的,有些由硬件和操作系统配合实现的,有些是由硬件实现的。
  • Cache从内存取数据时会预取一个Cache Line缓存起来,操作系统从硬盘读数据时会预读几个页面缓存起来,都是希望这些数据以后会被程序访问到。大多数程序的行为都具有局部性(Locality)的特点:它们会花费大量的时间反复执行一小段代码(例如循环),或者反复访问一个很小的地址范围中的数据(例如访问一个数组)。所以预读缓存的办法是很有效的:CPU取一条指令,我把和它相邻的指令也都缓存起来,CPU很可能马上就会取到;CPU访问一个数据,我把和它相邻的数据也都缓存起来,CPU很可能马上就会访问到。设想有两台计算机,一台有256KB的Cache,另一台没有Cache,两台计算机的内存都是512MB的,硬盘都是100GB的,虽然多出来256KB的Cache与内存、硬盘的容量相比微不足道,但访问Cache比访问内存、硬盘快几个数量级,由于局部性原理,CPU大部分时间是在和Cache打交道,有Cache的计算机明显会快很多。高速存储器的容量只能做得很小,却能显著提升计算机的性能,这就是Memory Hierarchy的意义所在。
  • 20
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值