硬件结构

1. 冯诺依曼体系结构

冯诺依曼结构对计算机的设计、研究和发展有重大影响,如图1所示,冯诺依曼结构主要包括以下三部分:

图1 冯诺依曼体系结构

中央处理单元:主要负责运算和逻辑控制,按照程序中的指令进行计算,并且根据条件执行程序中的不同部分,例如顺序结构,分支结构,循环结构。

存储器:负责存储指令和数据,保存程序执行过程中的中间结果等。主要包括寄存器、CPU缓存、内存。他们的容量逐渐增大,存储速度依次递减。计算机通常采用三级缓存结构。

输入输出:负责与外界进行交互,从外界获得输出,讲结果向外界输出。诸如键盘、鼠标、显示器、打印机以及网卡等外设,都属于此类设备。

尽管今天的计算机已经有了翻天覆地的变化,但从宏观看,现在主流的计算机依然采用冯诺依曼结构。

2. CPU与指令集架构

指令集架构(Instruction Set Architecture, ISA)是CPU与软件之间的桥梁,ISA包含指令集、特权级、寄存器、执行模式、安全扩展、性能加速扩展等诸多方面。

a. 指令集

指令集是ISA的重要组成部分,通常包含一系列不同功能的指令,用于数据搬移、计算、内存访问、过程调用等。CPU在运行操作系统或应用程序时,实际上就在执行他们被编译后的指令。常见的指令主要包括数据搬移指令(mov)、寄存器计算指令(如加法指令add、减法指令sub)、内存读写指令、跳转指令、过程调用指令、跳转指令、特权指令等。

b. 特权级

为了保护我们的批处理操作系统不受到出错应用程序的影响并全程稳定工作,单凭软件实现是很难做到的,而是需要 CPU 提供一种特权级隔离机制,使 CPU 在执行应用程序和操作系统内核的指令时处于不同的特权级。实现特权级机制的根本原因是应用程序运行的安全性不可充分信任。由于操作系统会被频繁访问,来给多个应用提供服务,所以它可能的错误会比较快地被发现。但应用自身的错误可能就不会很快发现。由于二者通过编译器形成一个单一执行程序来执行,导致即使是应用程序本身的问题,也会让操作系统受到连累,从而可能导致整个计算机系统都不可用了。所以,计算机科学家和工程师就想到一个方法,让相对安全可靠的操作系统运行在一个硬件保护的安全执行环境中,不受到应用程序的破坏;而让应用程序运行在另外一个无法破坏操作系统的受限执行环境中。为确保操作系统的安全,对应用程序而言,需要限制的主要有两个方面:

  • 应用程序不能访问任意的地址空间
  • 应用程序不能执行某些可能破坏计算机系统的指令

为了实现这样的特权级机制,需要进行软硬件协同设计。一个比较简洁的方法就是,处理器设置两个不同安全等级的执行环境:用户态特权级的执行环境和内核态特权级的执行环境。且明确指出可能破坏计算机系统的内核态特权级指令子集,规定内核态特权级指令子集中的指令只能在内核态特权级的执行环境中执行。处理器在执行指令前会进行特权级安全检查,如果在用户态执行环境中执行这些内核态特权级指令,会产生异常。

特权级也是ISA的重要组成部分,通常被称为异常级别(Exception Level, EL),总共有四种特权级别。

图2 CPU的四种特权级

  • EL0:最低的特权级,应用程序通常运行在此特权级,也称用户态。
  • EL1:操作系统通常运行在该特权级,也成为内核态。
  • EL2:在虚拟化场景下需要,虚拟机监控器(Virtual Machine Monitor,VMM)通常运行在该特权级。
  • EL3:和安全特性TrustZone相关,负责普通世界和安全世界之间的切换。

TrustZone已经被广泛应用,他从逻辑上将整个系统划分为安全世界和普通世界,计算资源可以被划分到这两个世界中。安全世界可以不受限制的访问所有的计算资源,而普通世界不能访问划分到安全世界的计算资源,比如说普通世界不能访问属于安全世界的物理内存和设备。

CPU在提供了四种特权级的同时也提供了特权级之间的切换的方式,通常用户态切换到内核态的方式主要有三种:

  • 应用程序需要调用操作系统提供的系统调用
  • 应用程序触发了异常,导致CPU从用户态切换到了内核态
  • 应用程序在执行的过程中,收到来自外设的中断。

前面两种场景主要是由于正在执行的指令引起的,属于内中断。第三种场景下CPU发生的特权级切换并不是由于CPU运行中的程序导致的,属于异步的CPU特权级切换,属于外中断。通常来说,CPU会保存当前的执行状态,以便操作系统内核在处理异常时使用并在处理结束后能够恢复应用程序的执行。操作系统为不同类型的异常配置了相应的异常处理函数,当发生特权级切换时,操作系统的相应的异常处理程序就会开始执行,在异常处理结束后,操作系统会恢复应用程序的上下文,并切换至EL0,使应用程序在相应的位置处继续执行。

a. 寄存器

寄存器是ISA的另一个重要组成部分。例如在EL1特权级下有两个页表基地址寄存器,他们负责翻译逻辑地址。

3. 物理内存和CPU缓存

CPU在执行过程中,可以在物理内存中写入数据或者读取数据。从CPU的角度看,物理内存实际上是一个大数组,每一个字节拥有一个物理地址,CPU可以在这个数组中存取数据。

相比于CPU的计算速度,内存访问速度是非常缓慢的,通常一条计算语句只需要几个时钟周期即可完成,但是一次内存访问可能会花上百个时钟周期。为了降低CPU访存的开销,所以在CPU中引入了缓存,CPU访问缓存的速度要快很多,最快只需要几个时钟周期,但是通常这种缓存价格昂贵,所以一般存储容量不大。由于空间局部性原理和时间局部性原理,加入缓存的机制是非常有必要的。图3展示了CPU缓存与内存结构。

图3 CPU缓存与内存结构
CPU缓存是由若干个缓存行组成的,每个缓存行包括有效位,用于标识缓存是否有效,标记地址,用于标记其对应的物理地址。通常CPU以缓存行为单位把物理内存中的数据读取到CPU缓存中。同样将数据写回到内存也是以缓存行为单位的。

4. 设备与中断

a.内存映射输入输出 内存映射输入输出是一种常见的CPU控制和访问设备的方式,主要的原理是将输入输出设备和物理内存放到同一个地址空间,为设备内部的内存和寄存器也分配相应的地址。CPU可以通过这些地址来访问设备中的内存数据和寄存器。当CPU通过内存映射输入输出的方式为设备分配了地址之后,CPU可以像访问内存一样去读写对应的设备。设备会通过总线监听CPU分配给自己的地址,然后完成相应的CPU访问请求。

b.轮询和中断 CPU通过内存映射输入输出的方式配置的地址可以获取输入,但是CPU如何知道有输入事件发生了?主要有两种方式,轮询和中断。轮询会导致CPU长时间处于等待外设输入的状态,造成CPU资源的浪费,另一种高效的方法是,当外设在输入后,主动告知CPU,然后CPU从再从相应地址中取出数据。中断机制赋予了设备通知CPU的能力,只需要在CPU中配置好相应的中断处理程序,该程序用于获取设备的输入即可。中断机制除了可以让设备通知CPU外,还可以让一个CPU核心通知另一个CPU核心。

内存映射输入输出可以使CPU主动的访问设备,中断使得设备可以主动的通知CPU,这两种机制的结合可以很好的实现CPU和设备之间的交互。

5. 一些相关的问题

a.CPU提供给软件的状态和指令有哪些?

CPU具有两个工作状态,主要是用户态和内核态,软件通常运行在用户态,它具有较低的特权级别。在这种状态下不能使用特权指令,不能直接使用系统资源,也不能改变CPU的工作状态,并且只能访问这个用户程序自己的存储空间。用户态不允许程序进行处理器中要求特权态的操作,以避免操作系统崩溃。每个进程都在各自的用户空间中运行,而不允许存取其他程序的用户空间。当程序需要使用系统资源时,就必须通过软中断机制进入内核态。内核态是操作系统的管理程序运行时的状态,它具有较高的特权级别。当处理器处于内核态时,它可以执行所有的指令,包括各种特权指令,也可以使用所有的资源,并且具有改变处理器状态的能力。提供的指令主要有数据搬移指令(mov)、寄存器计算指令(如加法指令add、减法指令sub)、内存读写指令、跳转指令、过程调用指令、跳转指令、特权指令等。

b.针对不同的软件场景(如应用程序、操作系统、虚拟化等),ISA提供了那些特权级?

EL0:最低的特权级,应用程序通常运行在此特权级,也称用户态。 EL1:操作系统通常运行在该特权级,也成为内核态。 EL2:在虚拟化场景下需要,虚拟机监控器(Virtual Machine Monitor,VMM)通常运行在该特权级。 EL3:和安全特性TrustZone相关,负责普通世界和安全世界之间的切换。

c.什么是异常、中断?异常是如何被处理的?

异常是指CPU内部出现的中断,即在CPU执行特定指令时出现的非法情况。同时异常也称为同步中断,因此只有在一条指令执行后才会发出中断,不可能在指令执行期间发生异常。产生的原因:1. 程序的错误产生的,编程异常通常叫做软中断(eg:除数为0)2. 内核必须处理的异常条件产生的(eg:缺页)。产生都不使用中断控制器,中断信号由指令直接给出。不能被屏蔽。并且异常没有自己的进程上下文,会用到当前进程的进程上下文。当CPU执行一个异常处理程序时,就不再响应其他异常和中断请求服务.如果此时发生了一个异常,CPU不能去响应它,又不能把它的信息丢失该怎么办呢?这是就用到了堆栈,把所有的信息压入栈。等当前异常处理后,才从堆栈中取出信息再响应刚才的异常。 中断也称为异步中断。因此它是由其他硬件设备依照CPU时钟信号随机产生,即意味着中断能在指令之间发生。中断主要是响应外部硬件设备的。产生通过中断控制器,中断信号是由中断控制器提供的。中断又分为外部可屏蔽中断(INTR)和外部非屏蔽中断,所用I0设备产生的中断请求均引起可屏蔽中断。硬件故障引起的故障则产生非屏蔽中断。中断使用自己的中断上下文,原来的进程上下文保持不变,而且可以返回中断之前所作的事件。在CPU执行一个异常处理程序时,就不再响应其他异常和中断请求服务.如果此时产生多个非屏蔽中断时,CPU的处理方法跟异常处理方法一样,使用堆栈。 操作系统中的异常处理是指在程序执行过程中发生了异常情况,例如内存读写错误、被零除、非法指令等,操作系统需要对这些异常进行处理,以避免系统崩溃或产生安全隐患。在操作系统中,异常处理通常采用异常处理向量表(Exception Vector Table)的方式实现。操作系统会在启动时为每一种异常情况分配一个唯一的异常号,并在异常处理向量表中为每个异常号分配一个对应的异常处理程序(Exception Handler)。当出现异常情况时,CPU会根据异常号寻找对应的异常处理程序并执行。异常处理程序的主要任务是诊断异常情况,采取必要的措施来恢复系统的正常运行。通常,异常处理程序会将异常情况的相关信息(例如错误代码、堆栈信息等)保存到系统内存中,然后执行相应的处理逻辑,可能包括重启系统、终止进程、回收资源等。

d. 虚拟内存和物理内存的区别是什么?是如何转换的?

物理地址:CPU地址总线传来的地址,由硬件电路控制其具体含义。 逻辑地址:现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要MMU(Memory Management Unit)的支持。MMU通常是CPU的一部分,如果处理器没有MMU,或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被内存芯片(物理内存)接收,这称为物理地址(Physical Address),如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address),而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将虚拟地址映射成物理地址。

e. 为什么要引入CPU缓存?直接访问内存会有什么问题?

由于相比于CPU的计算速度,内存访问速度是非常缓慢的,通常一条计算语句只需要几个时钟周期即可完成,但是一次内存访问可能会花上百个时钟周期。为了降低CPU访存的开销,所以在CPU中引入了缓存,CPU访问缓存的速度要快很多,最快只需要几个时钟周期,但是通常这种缓存价格昂贵,所以一般存储容量不大。由于空间局部性原理和时间局部性原理,缓存的命中率会很高,加入缓存的机制是非常有必要的。

f. CPU缓存的结构是什么样的?

CPU缓存是由若干个缓存行组成的,每个缓存行包括有效位,用于标识缓存是否有效,标记地址,用于标记其对应的物理地址。通常CPU以缓存行为单位把物理内存中的数据读取到CPU缓存中。同样将数据写回到内存也是以缓存行为单位的。

g. 如何访问CPU缓存?

CPU缓存与内存结构

h.CPU与设备直接的交互方式有哪些?

内存映射输入输出可以使CPU主动的访问设备,中断使得设备可以主动的通知CPU,这两种机制的结合可以很好的实现CPU和设备之间的交互。轮询也可以让CPU获取设备的数据。

i. CPU可以通过轮询或者中断的方式获取设备的输入,他们的差异有哪些?分别适合那些场景?

中断: 设备管理中,高速的处理器和低速的输入输出设备相对来说,会降低整体效率,为了减少程序直接控制方式中CPU的等待时间,提高系统的并行工作程度,采用中断处理方式是很有必要的。在I/O设备中断方式下,CPU与I/O设备之间数据的传输步骤如下:

  1. 在某个进程需要数据时,发出指令启动输入输出设备,准备要处理的数据;
  2. 在进程发出指令启动设备之后,该进程放弃处理器,等待相关I/O操作完成。此时,进程调度程序会调度其他就绪进程使用处理器。
  3. 当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转向预先设计好的中断处理程序,对数据传送工作进行相应的处理。
  4. 得到了数据的进程,转入就绪状态。在随后的某个时刻,进程调度程序会选中该进程继续工作。

中断方式的优点:I/O设备中断方式使处理器的利用率显著提高;支持多道程序I/O设备的并行操作,提高了效率。缺点:各种各样的输入输出设备通过中断处理方式进行并行操作,使中断次数增加,会造成CPU无法响应中断;如果在缓冲区装满数据之后发生中断。那么在数据传送过程中,发生中断的机会较多,将耗去大量的CPU处理时间。

轮询: 轮询是一种CPU决策如何提供周边设备服务的方式。在轮询过程中,由CPU定时发出询问,依序询问每一个周边设备是否需要其服务。每个设备都有一个指示命令就绪的位,指示该设备的状态。当此状态就绪即给予服务,服务结束后再问下一个周边,接着不断周而复始。轮询的优点:在一些应用中可以增强了程序的实时性。缺点:能处理的输入输出设备的数量也是有一定限度的。程序轮询占用CPU的处理时间,效率较低。

j. 中断的处理流程是什么?

中断处理流程图

k.从应用程序的视角看,异常和中断的区别是什么?

异常主要是应用程序中所执行的语句所触发的,不可以被屏蔽,中断是由外部设备触发的,分为可屏蔽中断和不可屏蔽中断。

l. 在发生特权级切换时,CPU会自动保存当前执行状态,包括程序计数器、栈指针等,请分析如果不保存程序计数器和栈指针会出现什么问题?

如果不保存执行状态,在异常处理完成后,操作系统无法恢复应用程序的上下文,当操作系统执行eret后(异常返回,Exception Return)指令切换至EL0,应用程序无法在被中断处继续执行。

m. 假设有一个很大的数组(远远大于缓存大小),请思考:再有缓存的情况下是随机读取这个数组上1字节的数据一万次快?还是连续读取一万字节快?请给出你的理由。

在有缓存的情况下,连续读取一万字节通常会比随机读取1字节快得多。在现代计算机系统通常采用层次化的存储结构,包括主内存(RAM)和缓存(通常是L1、L2和L3缓存)。这些缓存用于加速数据的访问,但它们有限的容量限制了它们可以存储的数据量。空间局部性:连续读取利用了局部性原理中的空间局部性。这意味着,当你读取一个地址的数据时,附近的地址的数据通常也会被加载到缓存中。因此,当你连续读取一段内存,缓存可以有效地预取连续块的数据,从而减少了访问主内存的次数。缓存行大小:缓存通常以固定大小的缓存行(cache lines)来工作,每个缓存行可以存储一定数量的连续数据。如果你连续读取数据,那么很可能会命中相同的缓存行,这意味着你可以多次重复使用相同的缓存数据而不必每次都访问主内存。减少缓存失效:随机读取数据会导致更多的缓存失效,因为每次读取都需要加载不同的数据块,这会使缓存中的有效数据被替换。相比之下,连续读取减少了缓存失效的机会,因为它们倾向于访问相邻的数据。存储系统特性:硬件和操作系统通常会针对连续访问进行优化,以最大限度地提高吞吐量。磁盘驱动器、文件系统以及存储子系统都会考虑连续访问来提高性能。总之,连续读取一万字节在有缓存的情况下通常更快,因为它更好地利用了缓存的工作原理,减少了主内存访问次数和缓存失效。

n.轮询是否在任何情况下都比中断差?请结合具体的例子进行分析。

轮询(Polling)和中断(Interrupt)都是用于处理外部事件或设备状态变化的计算机系统中常见的方法。 轮询(Polling):轮询是一种主动查询的方法,它通过周期性地检查外部事件或设备状态来确定是否需要处理。这种方法通常通过循环来实现,不断地检查设备状态是否发生了变化。 例子:键盘输入 假设我们正在编写一个简单的控制台程序来接受用户的键盘输入。使用轮询的方式,程序将不断地检查键盘缓冲区,以查看是否有新的字符输入。如果有字符输入,程序将立即处理它。

中断(Interrupt):中断是一种事件驱动的处理方式,当外部事件发生时,硬件或操作系统会中断正在执行的程序,将控制权转交给相关的中断服务程序,以处理事件。 例子:外部设备中断 考虑一个网络服务器,它需要处理传入的网络请求。当网络请求到达时,系统会生成一个中断,将控制权传递给网络请求处理程序。

相关推荐
Redstone Monstrosity30 分钟前
字节二面
前端·面试
UestcXiye2 小时前
面试算法题精讲:求数组两组数差值和的最大值
面试·数据结构与算法·前后缀分解
严格格2 小时前
三范式,面试重点
数据库·面试·职场和发展
WG_173 小时前
C++多态
开发语言·c++·面试
鱼跃鹰飞4 小时前
Leetcode面试经典150题-130.被围绕的区域
java·算法·leetcode·面试·职场和发展·深度优先
鱼跃鹰飞14 小时前
Leetcode面试经典150题-349.两个数组的交集
算法·leetcode·面试
程序猿进阶20 小时前
如何在 Visual Studio Code 中反编译具有正确行号的 Java 类?
java·ide·vscode·算法·面试·职场和发展·架构
无名之逆21 小时前
云原生(Cloud Native)
开发语言·c++·算法·云原生·面试·职场和发展·大学期末
andrew_12191 天前
腾讯 IEG 游戏前沿技术 一面复盘
java·redis·sql·面试
andrew_12191 天前
腾讯 IEG 游戏前沿技术 二面复盘
后端·sql·面试