甄建勇:五分钟解决一切系列-打通软硬件的连接引言。许多人都有这样的经历:下班回家,准备好了饭菜,刚坐下吃饭,顺手点开旁边的iPad或手机(以下称为计算机),想继续看昨天没有看完的电视剧。

2024-07-01 09:53:07 作者:6kYzQ!yIEmp_M6UkZ
你知道吗?当你轻松地进行某些操作时,比如只是稍微调整一下,计算机内部究竟发生了哪些不可思议的过程,才能呈现出新的情节和画面呢?今天,我们假设一个小人,名叫小土孩儿。她将跟随着你的指尖滑入计算机内部,看看会遇到一些你有可能知道,也可能不知道的东西......首先,小土孩儿离开你的手指后,第一个停靠的地方是键盘。毫无疑问,从技术上来说,屏幕的外层是一个触摸屏,其实质与计算机的键盘是相似的,它们的功能都是捕捉用户的操作。矩阵键盘内置扫描电路,定时进行扫描,根据电平信号的变化,判断是否有按键被按下。当小孩的手指落在键盘上时,会给按键(假设是空格键)施加一定的压力,这种压力会使得空格键下方的电路闭合通路,从而在下一次扫描时,键盘的扫描电路就会检测到这种状态。对于一次单击操作,我们通常认为自己按了一次键,但实际上,键盘扫描电路会存在防抖机制,即在一定时间内,某个按键在持续没按下的情况下才算是一次单击。如果点击速度过快,防抖机制可能会将其识别为误操作。如果按压时间过长,可能会被识别为“双击”或“长按”。我们可以根据人类一般操作速度来确定合适的扫描时间间隔。在按下空格键后,键盘控制芯片会将空格键的编码保存在一个寄存器中,同时会拉低与处理器(CPU)连接的线,向处理器发送一个外部中断信号。当CPU内部的中断控制器接收到外部中断信号时,会设置CPU内部的一个控制寄存器的值为1,以示已接收到外部中断。中断控制器内部还有另一个控制寄存器,用于指示哪些中断应该被屏蔽,以及哪些中断需要CPU进行处理。经过屏蔽处理的中断会被附着在CPU内部正在执行的指令上,表示该指令执行时发生了中断异常。我们知道现代CPU通常采用多级流水线进行指令处理。因此,我们需要确定一个适当的中断或异常附着点。由于CPU采用了乱序执行技术,我们需要在指令顺序被打乱之前进行附着。因此,一般情况下附着点是在指令译码阶段。

这个异常附加的指令会随着CPU流水线从译码阶段开始依次传送到下一个阶段。在传递过程中,异常添加的指令不会传送到执行单元。举例来说,如果附着的是一条ADD指令,那么该指令在正常情况下会被发送到加法器以执行加法运算。当这个指令到达ROB(重新排序缓冲区)时,CPU将会开始处理异常情况。在

CPU内部,指令流水一般由多个流水级组成,包括分支预测、取指、初级译码、保留站、乱序发射、再次译码、指令执行和RoB等。在程序中,通常会包含大量的条件分程序中,通常会包含大量的条件分支,通常会包含大量的条件分支语通常会包含大量的条件分支语句包含大量的条件分支语句,含大量的条件分支语句,其执行结果大量的条件分支语句,其执行结果决定了接量的条件分支语句,其执行结果决定了接下条件分支语句,其执行结果决定了接下来语句,其执行结果决定了接下来要执行结果决定了接下来要执行执行结果决定了接下来要执行的行结果决定了接下来要执行的指的指定了接下来要执行的指令。要想知道分支指令的结果,必须在执行阶段才能获得。在这种情况下,我们有两个选项:

1. 等待分支指令的结果,然后再继续执行分支指令后面的指令。1. 这句话的意思是如果没有发现兔子,猎鹰也不会展翅飞翔。 \n2. 你可以预测分支指令的执行结果,根据预测的结果,提前读取分支指令后面的指令。投机执行可以理解为利用市场波动或法律漏洞谋取不正当利益的行为。很明显,若采用“不见兔子不撒鹰”的处理方式,就会导致分支指令前方的流水线产生空泡,进而降低CPU的性能表现。贸然采取投机执行的后果是,假如猜错了,那么就需要引入相应的错处理逻辑。为了增加猜中率,CPU开始使用分支预测机制。

分支预测是基本的分支预测算法,非常简单,只需要引入一个饱和计数器来预测分支结果。例如,可以使用一个2位的饱和计数器来进行if条件判断分支指令的预测。其中,0和1代表强猜测跳转,2和3代表强猜测不跳转。它的运行原理如下:

一开始,将该计数器设为1,代表弱跳转。如果猜错,计数器就会减1,然后进行跳转。若未猜中,则计数器会加1,变为2,表示弱无需跳转。当计数器达到0后,即使继续猜中,计数器将保持在0,也就是说在达到饱和状态后,持续猜对时,计数器的值不会变化。采用

机制虽然简单直接,但存在一个重大缺陷,就是猜中率可能为0%,因为可能会在1和2之间摇摆不定。为了解决这一问题,CPU的分支预测机制采用了更为复杂的方法。根据PC(程序计数器)的数值,将地址对应的指令读取到CPU内部,这样

的取值就是

。通常指令被存储在外部存储设备中,路径曲折且漫长。为了加快数据获取速度,通常会在CPU中加入指令缓存和内存管理单元(MMU)。之前我们曾经讨论过关于缓存和内存管理单元(MMU)的问题,请参考甄建勇的“五分钟搞定”系列文章。将上来的指令经过译码处理后,会被发送到译码单元。即,指令的解析。对于采用RISC指令集的CPU而言,其译码器相对简单。CISC指令集的译码非常复杂,因为同一条指令在译码时,指令后面的内容会受到指令前面译码结果的影响。举例来说,为了加快处理速度,英特尔的x86处理器的解码单元必须采取“广泛搜索,重点培养”的策略,即同时解码所有可能的选项,然后根据一部分解码结果来确定正确的选项。在实际CPU中,最前端的译码单元只需要对指令的部分内容进行译码,就可以确定指令的目的地,因此无需在一开始就对全部指令进行译码。举个例子,只要区分出指令类型,就可以将其发送到下一阶段。执行指令的解码可以由相应的执行单元完成。

保留站中保留着 的流水级别,就好像一条狭窄的小巷,为了保持指令的次序,指令们在小巷里排队,缓慢地前进。我们了解到,在巷子里的指令中,有一些指令之间存在依赖关系,而有些则是独立的。

保留站就好比是巷子尽头的一个宽阔广场,在这里那些原本处在队尾但却不需要等待前方指令的指令可以提前执行,即指令超越。寄存器重命名和增加寄存器数量可解除WAW依赖\nWAW依赖是指令序列中的1和3等情况引起的寄存器相关依赖。寄存器数量不足,导致这种依赖的产生。但只要增加寄存器数量,就可进行寄存器重命名,从而消除指令之间的依赖,让两条执行流同时执行。就好像我们去餐馆用餐,结果发现需要排队,而排队的原因居然是因为餐馆只备了一副筷子。重构并改写文本如下:\n

在计算机科学中,有三种类型的数据依赖关系。第一种是真实依赖,也称为“读后写”依赖。它表示指令必须等待来自先前指令结果的可用性,才能继续执行。第二种是输出依赖,也称为“写后写”依赖。该依赖表示两个指令在其结果写回相同寄存器或内存位置上发生冲突。最后一种依赖是反向依赖,也称为“写后读”依赖。它表示一条指令在另一条指令执行之后修改该位置值,但是先执行的指令需要读取该位置值。\n除此以外,重命名是一种优化技术,可以减少依赖和提高并行性。在该技术中,新的寄存器分配用于存储计算中间结果,消除了输出依赖和反向依赖。最后,我们还可以通过流水线技术来进一步提高并发性。例如,ADD R3, ... 就可以在下一条指令执行之前的阶段开始执行。R1、R2

将其相加并存至R3。R1、R2

 

的2

存至addr0处,R3

存至addr0处。R3

 

3

将以下内容改写和表述:\nADD R3, R4, R5\nADD R60, [R4+#], R3R5

R3 -> R60

4

将R3的值存储到地址0

将值存储到地址0经过寄存器重命名的处理,R60 会被保留在保留站内等待指令发射 。一旦操作数准备好,它们就会被发射到相应的执行单元。很明显,这种发射可以是不按顺序的,可能会发射一条以上的指令。也就是说,混合执行和同时发射技术。乱序执行和多发射技术,对指令的执行有重要影响。实现指令的执行,就如同“八仙过海”。执行过程在不同的指令下会有很大的差异。我们曾经讨论过“甄建勇_五分钟内搞定_1+1=?”.本系列文章详细阐述了计算机中加法器、乘法器的具体实现细节,如有兴趣的同学可进行参考。「

ROB

」这句话意味着「出来混,迟早要还的」,ROB就像我们要偿还之前「乱序」的账目。当指令进入保留站时,我们需要记录它们的先后顺序。等到乱序发射和执行的指令完成后,它们会进入ROB,也就是另一个“广场”。为了维持程序原有的正确顺序,我们需按进入保留站的顺序,逐一移除ROB中的指令,即进行指令的提交。在这里处理的还有最初提到的被恶意附加指令的异常情况。ROB的另一个目的是为操作系统提供“准确异常”功能。也就是说,在处理异常之前,先执行异常指令之前的所有指令,然后取消异常指令之后的所有指令。对于此目的,ROB被设计来实现。在上述内容中,讨论了在发生CPU异常时的处理。当ROB收到附带异常的指令时,会执行一系列操作:首先向CPU的其他模块发送指令取消信号,取消所有异常之后的指令。接下来需要储存与附加指令相关的程序计数器(PC)值。另外,需要更改CPU内部的控制寄存器,将CPU转为内核态(CPU通常有多个状态,如内核态、用户态、调试态等),接着,将PC值设为异常向量的入口地址,再从入口地址处读取值,开始执行该处指令。

操作系统异常处理

\n异常处理是一个复杂的过程,需要软硬件协同完成。前文所述为硬件异常处理,当CPU开始执行异常处理程序时,将会按照操作系统事先设定的操作进行处理。一般情况下,异常入口处的指令通常是跳转指令,不同的异常入口处都有对应的跳转指令,这些跳转指令紧密相连,就像一个向量一样,因此被称为“异常向量”。异常向量是操作系统的组成部分之一。因此,当CPU执行异常向量时。一旦启动,操作系统的代码便会被执行。 一般处理OS异常时,首先会保存处理器的现场,并读取CPU内部的控制寄存器,即前文提及的寄存器。重新审视后发现是外部干扰造成的故障,操作系统会接着读取外部干扰控制器的存储单元,并在同时清除中断。当重新阅读时发现键盘被按下,于是继续扫描键盘控制器的寄存器,发现被按下的是空格键。

OS要接下来找出需要使用空格键的进程,也就是要确定空格键要发送给哪个进程。经过OS查询,发现一个名为X奇艺的视频软件正在等待按键输入,因此发送了空格键的数值给X奇艺,并唤醒了X奇艺进程。当

用户态程序执行 时, X奇艺程序被唤醒后,发现操作系统传送过来的是一个空格键的键值。假设X奇艺程序的预设条件是,当处于暂停播放状态时,按下空格键表示视频将继续播放。在接下来的过程中,X奇艺将读取即将呈现的数据,此数据可以来自网络或本地磁盘,然后调用视频解码器的驱动程序。驱动程序会设置解码器的寄存器,并让解码器开始工作解码。如果即将显示的内容涉及矢量数据并需要GPU参与渲染,那么就需要根据矢量数据进行渲染。GPU的渲染功能是通过给定虚拟相机、3D场景中的物体以及光源等要素来生成2D图像。GPU渲染图像的过程分为多个阶段,包括顶点数据输入、顶点着色、曲面细分、几何着色、图元组装、裁剪剔除、光栅化、像素着色以及测试与混合。顶点数据通常包含顶点坐标、法线、颜色和纹理坐标等。当

顶点着色器(Shader)接收到顶点数据后,其主要功能是进行坐标变换。这意味着将局部坐标转变为世界坐标或观察坐标。曲面细分(Tessellation)是一种将输入的较大三角形切分成更小三角形的技术,其目的是对于离摄像机近的物体细节进行更加丰富的呈现,而对于离摄像机远的物体则降低细节的呈现。

几何着色技术可以将输入的点、线等基础图元独立运用,并将它们合成多边形。图元素装配(Primitive Setup)阶段的主要任务是按照规则将原始的图元组装成特定的图元。例如对于超出裁剪窗口范围的图形进行裁剪,移除不可见的背面图形等操作,可以减少后续阶段需要处理的图形数量。

光栅化的主要功能是将三维的连续物体转换为离散的屏幕像素点。在像素着色阶段,每个像素的最终颜色会根据光照等因素进行决定,并进行阴影处理。我们所看到的一些炫酷效果主要来源于像素着色阶段。GPU渲染的最后阶段是检查,此阶段包括裁剪检查、模板检查、深度检查等。只有经过测试的像素才会被包括在混合中,例如Alpha混合。Alpha值用来表示物体的透明度程度,当Alpha=1时,表示完全不透明。经过上述繁复的渲染管道,GPU成功渲染了一幅图像,并将渲染的图像数据写入可供显示控制器读取的帧缓冲区。图像的呈现和显示是一项复杂的过程。图像的格式繁多,例如CIE的XYZ、LUV、LAB等。RGB包括了RGB、sRGB、AdobeRGB、scRGB、DCI-P3、Rec.709、ACES等。YUV、YIQ、YpbPr、YCbCr、xvYCC、IPT、ICtCp等是基于Luma+Chroma的不同视频颜色空间。HSV和HSL是Hue+Saturation的两种色彩模式。CMYK是一种在打印机领域被广泛采用的颜色模式。每种格式都有相应的色彩空间,下图展示了BT标准中的两种色彩空间。为了让显示器所显示的图像与真实世界中的物体看起来一致,我们特别制定了多种格式标准

。随着技术的不断进步,显示器也在不断升级。

未来的显示器可能会变成这个样子:

但事实上,为了使图像格式与显示器兼容,还需要定义一种数据传输协议,例如HDMI。同样的,为了使CPU与GPU协同工作,也需要定义一种数据传输协议,例如PCIe。

 

显示器的控制器从帧缓冲中读取GPU处理后的图像数据,通过连接显示器的总线传输至显示器内部的控制器,最终控制显示电路,将图像呈现在屏幕上。

甄建勇是一位高级架构师,在某国际知名大厂工作,拥有超过十年的半导体行业工作经验。我的主要研究领域是CPU/GPU/NPU架构与微架构设计。我对经济学、心理学和哲学这三个领域充满兴趣。此文摘自微信公众号「Linux阅码场」,扫描下方二维码即可关注。请联系Linux阅码场公众号获取转载本文的授权。

 

在线咨询 拨打电话

电话

02088888888

微信二维码

微信二维码