在上一篇文章中,介绍了核算机中心组件之一担任存储的内存,在这篇文章中,将会介绍另外一个中心:担任核算的CPU。这个由一个个晶体管组成的每秒可以进行数十亿次,上百亿次核算的核算机的发动机。
1 CPU 的组成
1.1 从晶体管到ALU和信息存储
在大学数电的课上,晶体管组成的与门,或门,非门,与非门等等构成的逻辑电路组合,只要奇妙安排,就可以规划成很多杂乱的电路,完结核算。这些晶体管组成了CPU 中专门担任运算的模块,这便是算术逻辑单元ALU。
同样,晶体管经过奇妙的规划组合,也能完结信息存储。
这样具备存储才能的组合电路便是咱们在上几篇文章中都有说到的寄存器。在此基础上建立起愈加杂乱的电路存储更多的信息,一起提供寻址功用,可以读取信息,这样,内存也诞生了。
一起,也可以看出,寄存器,内存的实质都是电路,也便是说,一断电,保存的信息都丢掉了。这也便是一些核算机在没保存文件的状况下断电重启后,数据丢失的原因。
1.2 软件与硬件的接口:指令集
指令集便是定义了一台核算机可以了解和履行的指令调集。 每个指令都告诉核算机履行特定的操作,例如履行算术运算、逻辑运算、内存拜访、分支和跳转等。
指令集是核算机的底层编程接口,它确认了核算机可以履行的一切操作。 换句话说,指令集是软件与硬件的交汇点,在指令集之上,是软件的世界,在指令集之下,是硬件的世界。
1.3 时钟信号
现在,咱们的电路具备了核算才能,贮存才能,还可以经过指令告诉电路应该履行什么操作,可是现在,咱们要靠什么和谐或许靠什么来同步各个部分的电路让它们协同作业呢?
答案便是在数电讲堂上学过的时钟信号,时钟信号每改动一次电压,整个电路中的各个寄存器也便是整个电路的状况就会更新一下,这样就能保证整个电路都是协同作业。
咱们常说的CPU 主频便是在一秒钟内时钟信号的频率,明显主频越高,CPU 在一秒钟内完结的操作也就越多。
从此,有了可以完结各种核算的ALU,可以存储信息的寄存器,以及控制他们协同作业的时钟信号,他们在一起,就成为了CPU,从此,人类具有了人类的第二个大脑。
2 检查CPU 的状况
在电脑上可以检查自己核算机CPU 的运用状况,以masOS 体系为比方:
打开活动监视器,点进CPU,就可以看到CPU 的运用状况:
- “System” 表明体系进程占用的 CPU 百分比,这些是由 macOS 体系管理的后台使命和进程。
- kernel_task:这是 macOS 内核的一部分,它履行比方硬件管理、内存管理和体系资源分配之类的使命。kernel_task 一般会占用必定的 CPU 时刻,特别是在处理体系级使命时。
- WindowServer:这是担任 macOS 图形用户界面的进程。它处理窗口、显现和用户界面元素的烘托,因而在图形密集型使命时或许会消耗一些 CPU 资源。
- mds 和 mdworker:这些是用于 Spotlight 搜索和文件元数据的进程。它们可以在后台履行,占用必定的 CPU 资源。
- “User” 表明用户进程占用的 CPU 百分比,这些是由应用程序履行的使命和进程。
- Google Chrome:正在运用 Google Chrome 阅读网页,Google Chrome 进程将归类为用户进程,并显现其 CPU 占用百分比。这表明阅读器正在履行使命,如加载网页、运转插件或履行 JavaScript 代码。
- Bilibili:正在运用Bilibili 观看视频,也将归类为用户进程,并显现其 CPU 占用百分比。
- “Idle” 表明 CPU 的闲暇时刻百分比,即 CPU 未被任何使命或进程运用的时刻。
可以看到,CPU 的闲暇时刻占了最大的部分,即 87.38%。这意味着大部分时刻内,CPU 都处于闲暇状况,没有被使命或进程占用,这是正常的状况。
Windows 体系也有相似这种使命管理器,可以检查CPU 占用状况。
在Windows 体系下,会有一个专门的“体系闲暇进程”,这个进程是运用率最高的线程,便是在CPU 闲暇时运转的线程,这样规划是为了避免队列判空,为了能让调度器总能在进程就绪队列中找到一个可供履行的线程。
相比之下,在 macOS 中,体系资源管理的方法略有不同。macOS 运用名为 “Mach”的微内核,它担任管理体系的使命和资源。Mach 内核经过多线程和调度算法来处理资源分配,而不像 Windows 那样专门为“体系闲暇进程”创立一个独自的进程。macOS 的资源管理一般愈加动态,依据需求自动分配资源,以保证体系响应和功能。
3 CPU:流水线形式履行机器指令
3.1 CPU 处理机器指令
CPU 采用流水线形式履行机器指令的主要目的是进步指令履行的功率和功能。流水线处理是一种将指令履行过程分解为多个连续阶段的规划方法,每个阶段履行特定的使命。
流水线形式并没有削减一条指令履行的时刻,可是进步了吞吐量:在流水线中,每个阶段都可以一起处理不同的指令,因而多个指令可以在不同的阶段一起履行。这导致了指令的堆叠履行,从而进步了指令吞吐量。所以即使每个指令的履行时刻没有变化,全体体系的功能也有所提升。
CPU 履行一条指令有四个过程:取值,译码,履行,回调。这几个阶段也会由特定的硬件来完结。
- 取指 :从内存中获取下一条指令。
- 译码 :解析指令的操作码和操作数,确认指令要履行的操作。
- 履行 :履行指令中的操作,或许触及算术运算、逻辑运算等。
- 回调 :将履行成果写回到寄存器文件或内存中。
3.2 if 句子和分支猜测
分支猜测是CPU 中的一项重要技能,用于进步程序履行的功率。它主要用于处理条件分支句子(if-else句子或循环中的分支)带来的功能问题。
假如有这样一个场景:咱们创立了一个巨细为10000的数组,再遍历这段数组,对里面假如大于128的数字逐一相加,那么假如这个数组是有序的,比方[1, 2, 3, 4, …, 10000],那么这段代码的运转时刻要比无序的数组,比方[67, 22, 12, 8, …, 8789]要快。
这便是CPU 以流水线的方法履行机器指令和分支猜测导致的。
在流水线形式下,假如遇上了if 句子,那么就有或许if 指令还没有履行完结,后续的指令就要进入流水线了,那么这个时分,为了不浪费CPU 的资源,就会猜测后一条指令应该是什么。假如猜对了,那么流水线继续前进,假如猜错了,那么流水线上的现已履行的过错指令悉数报废,明显,经常猜测错,就会有功能损耗。
这也便是上面那个比方为什么有序的数组比无序数组需求的履行时刻更少,有或许代码是这样写的:
if (num[i) > 128) {
sum += num[i];
}
有或许CPU 就会分支猜测num[i] 是大于128的,假如是有序数组,当时一个数现已大于128,后面的数悉数就会猜测成功,可是相反,假如是无序数组,猜测的准确度就会大大下降,尤其是这个比方中有10000个数,时刻距离会愈加大。
所以,咱们得到了一个进步程序功能的办法:假如要运用if 句子,那么最好让程序可以猜对这个条件。
4 CPU 的指令集
在刚刚咱们说到了指令集是核算机可以了解并且履行的指令的调集,是软件和硬件的接口。那么指令集可以说是CPU 的“才能调集”,这又产生了两种不同的指令集:杂乱指令集CISC 和精简指令集RISC。
4.1 杂乱指令集CISC
这是最先出现的,是核算机科学家为了使指令能将愈加便于编写(那个时分大部分程序都直接运用汇编言语编写),就要使机器指令愈加接近人类笼统的言语,也便是使一句指令背后的意思愈加“杂乱”,就发明了杂乱指令集CISC
CISC 有下面几种特点:
- 愈加笼统和杂乱:一条指令集可以尽或许完结更多的使命
- 机器指令的长度不固定
- 机器指令高度编码,进步代码密度
一起,由于一条指令集可以尽或许完结更多的使命,所以或许需求较少的内存拜访来完结一项使命,这在某些状况下可以进步功能。
4.2 精简指令集RISC
这个年代下,程序员越来越多运用高档言语写程序,依靠编译器来自动生成汇编指令,一起有人发现,CISC 有一部分杂乱的指令并不经常用到,并且规划编译器的人也愈加倾向把高档言语翻译成简略的汇编指令而不是杂乱笼统的指令。
因而产生了精简指令集RISC。
相比于CISC,RISC的
- 指令集愈加简略:RISC 架构的核算机具有较小、更简略的指令集,这些指令履行的操作一般都十分根本,如加载、存储、算术运算和逻辑运算。每条指令都被规划成在一个时钟周期内履行。
- 履行速度快:由于每个指令的履行时刻短且硬件简略,RISC 处理器一般可以以更高的时钟频率运转,从而提供更高的功能。
CISC 架构着重指令集的杂乱性和灵活性,而 RISC 架构着重硬件的简洁和履行功率。
在后期,CISC 架构也开始变得像RISC:在编译器生成的指令还是CISC,可是在CPU 内存履行指令时,采取了相似RISC 的方法,这样既能坚持CISC 指令向前兼容,又能获取RISC 的好处。
直到如今,Intel 和Windows 的wintel联盟在核算机商场占有着一席之地,以x86为代表的CISC 处理器在服务器端和桌面端占有重要地位。
而基于RISC 的ARM 架构也由于高的履行功率而统治着移动端商场,在桌面端,Apple 也研发了基于RISC 的M1 芯片,在Mac 核算机中也取代了Intel 的芯片。
从可猜测的未来来看,CISC 和RISC 将会长久的共存下去。
5 寄存器
在第二篇文章:核算机底层2 程序在运转时发生了什么中说到,PC 寄存器(程序计数器)速度更快,容量也更小,CPU 拜访内存的速度大概是拜访寄存器速度的1/100,在创立进程时,代码以及代码依靠的数据被加载到内存,履行机器指令时,需求把内存中的数据搬运到寄存器中,供CPU 运用。实际上,寄存器和内存没有什么不同,都是用于贮存信息的,只不过寄存器速度更快,造价更高,因而容量较小,是一个临时存放点。
依据用处,寄存器也有好几种。
5.1 栈寄存器(Stack Pointer)
在第三篇文章:核算机底层3 内存中说到,函数在运转时都有一个运转时栈,关于栈来说,最重要的便是栈顶信息,栈顶信息就保存在栈寄存器中,除此之外,栈寄存器中也有局部变量,函数调用和回来地址。
5.2 指令地址寄存器(Program Counter)
也便是咱们熟悉的PC 寄存器(程序计数器),当程序启动时,第一条要被履行的指令会被写入PC 寄存器中,这样CPU 需求做的便是依据PC 寄存器中的地址区内存中取出指令并且履行。PC 寄存器中的指令地址也会不断递增,是CPU 下一条指令的地址。
5.3 状况寄存器(Status Register)
状况寄存器是用来保存状况信息的,例如,在算数运算中,或许会产生进位或许溢出,这些信息就保存在状况寄存器中。
除此之外,CPU 履行机器指令时的两种状况:内核态和用户态,状况寄存器中也有特定的比特位来符号当时CPU 正作业在哪种状况下,因而咱们就可以知道当时CPU 正作业在那种状况下。
6 CPU借助栈来完结指令
栈是咱们熟知的一种先进后出(FILO)的数据结构,CPU 处理使命特别是各种嵌套式的使命,便是奇妙利用了栈。
6.1 函数调用与运转时栈
这是咱们在上一篇文章核算机底层3 内存的栈区中详细讲过的,每个函数在运转时都有属于自己的栈帧,当函数A 调用函数B 时,会将运转时信息保存在函数A 的栈帧中,当函数B 运转完后,会依据栈帧中的信息(上下文信息Context)康复函数A 的运转,这整个调用的次序,便是栈的先进后出的次序。
6.2 体系调用与内核态栈
当咱们进行读写磁盘或许创立新的线程时,是用户程序经过体系调用操作体系内部经过调用一系列函数来处理请求的,有函数调用就有运转时栈,而操作体系完结体系调用的运转时栈在 内核态栈(Kernel Mode Stack) 中。
每个用户态线程在内核态都有一个对应的内核态栈。开始时,程序运转在用户态,此刻运转到了体系调用的代码,体系调用指令的履行将会触发CPU 状况的切换,此刻CPU 从用户态切换到内核态,找到该用户态线程对应的内核态栈,这个时分,用户态线程的上下文信息Context 就会被保存在内核态中。
此后,CPU 开始履行内核中的相关代码,后续内核态栈会像用户态运转时栈相同,跟着函数的调用和回来增加及削减。
当体系调用履行完后,依据内核态栈中保存的用户态程序上下文Context 康复CPU 状况,并从内核态切换回用户态,这样用户态线程就可以继续运转了。
6.3 中止与中止函数栈
核算机在运转程序时,也可以处理鼠标点击,键盘按动,接收网络数据等使命,便是经过中止处理函数来完结的。
中止的实质是打断当时CPU 的履行流,跳转到具体的中止处理函数中,当中止处理函数履行完结后,再跳转回来。
中止处理函数有自己的运转时栈吗?
答案是分两种状况:
- 中止处理函数没有自己的运转时栈,这种状况下,中止处理函数依靠内核态栈来完结中止处理。
- 中止处理函数有自己的运转时栈(ISR栈),这种状况下,每个CPU 都有自己的ISR 栈。
简略地以第一种状况为例,中止处理函数与体系调用比较相似,只不过体系调用时用户态程序主动建议的,而中止处理是由外部设备建议的,也便是CPU 在履行任何一条机器指令时,都有或许因中止而暂停当时程序的履行,转而去履行中止函数。
6.4 线程切换与内核态栈
假定现在体系中有两个线程A 和B,现在CPU 需求在两个线程之间切换处理使命。
每个Linux线程中都有一个对应的进程描述符task_struct
,在该结构体内部,有thread_struct
专门用来保存CPU 的上下文信息。
当CPU 从线程A切换到线程B时,就先将履行线程A 的CPU 上下文信息保存到线程A 的描述符中,然后像线程B的描述符中保存的上下文信息康复到CPU 中,这样,CPU 就可以履行线程B了。
7 总结
从简略的晶体管一步步到杂乱的CPU,ALU 带来了核算才能,寄存器带来了存储才能,时钟信号带来了和谐统一才能,指令集确认了核算机可以进行的一切操作,是软件与硬件的接口。杂乱指令集CISC 和精简指令集RISC 别离统治着桌面端,服务器端和移动端。不同的寄存器有不同的用处,栈寄存器保存着栈最重要的栈顶信息,PC 寄存器保存着CPU 下一条要履行的指令,状况寄存器保存着CPU 目前的状况。函数调用,中止处理,线程切换,体系调用都离不开上下文Context 的保存和康复,而Context 的保存与康复又是借助于栈这种数据结构。
8 下一篇文章
核算机底层5 缓存cache
9 参考资料
- 陆小风.核算机底层的隐秘. 电子工业出版社, 2023.
- 核算机底层的隐秘 gitbook