操作系统之CPU!
操作系统之CPU!
月伴飞鱼冯诺依曼模型中 CPU 负责控制和计算。
为了方便计算较大的数值,CPU 每次可以计算多个字节的数据。
如果 CPU 每次可以计算 4 个 byte,称作 32 位 CPU。
如果 CPU 每次可以计算 8 个 byte,称作 64 位 CPU。
这里的 32 和 64,称作 CPU 的位宽。
CPU指令集权限
Inter把
CPU指令集
操作的权限由高到低划为4级:
- ring 0
- ring 1
- ring 2
- ring 3
其中 ring 0 权限最高,可以使用所有
CPU 指令集
。ring 3 权限最低,仅能使用常规
CPU 指令集
,不能使用操作硬件资源的CPU 指令集
。
- 比如
IO
读写、网卡访问、申请内存都不行,Linux系统仅采用ring 0 和 ring 3 这2个权限。ring 0被叫做内核态,完全在操作系统内核中运行
ring 3被叫做用户态,在应用程序中运行
寄存器
CPU 要进行计算,比如最简单的加和两个数字时,因为 CPU 离内存太远。
- 所以需要一种离自己近的存储来存储将要被计算的数字。
这种存储就是寄存器。
寄存器就在 CPU 里,控制单元和逻辑运算单元非常近,因此速度很快。
寄存器中有一部分是可供用户编程用的,比如用来存加和指令的两个参数,是通用寄存器。
还有一部分寄存器有特殊的用途,叫作特殊寄存器。
比如程序指针,就是一个特殊寄存器。
它存储了 CPU 要执行的下一条指令所在的内存地址。
- 注意,程序指针不是存储了下一条要执行的指令,此时指令还在内存中,程序指针只是存储了下一条指令的地址。
下一条要执行的指令,会从内存读入到另一个特殊的寄存器中,这个寄存器叫作指令寄存器。
指令被执行完成之前,指令都存储在这里。
寄存器的数量通常在几十到几百之间,每个寄存器可以用来存储一定字节(byte)的数据。
32 位 CPU 中大多数寄存器可以存储 4 个字节;
64 位 CPU 中大多数寄存器可以存储 8 个字节。
总线
CPU 和内存以及其他设备之间,也需要通信,因此我们用一种特殊的设备进行控制,就是总线。
总线分成 3 种:
地址总线:专门用来指定 CPU 将要操作的内存地址。
数据总线:用来读写内存中的数据。
- 当 CPU 需要读写内存的时候,先要通过地址总线来指定内存地址,再通过数据总线来传输数据。
控制总线:用来发送和接收关键信号,比如中断信号,还有设备复位、就绪等信号,都是通过控制总线传输。
- 同样的,CPU 需要对这些信号进行响应,这也需要控制总线。
存储器分级
当 CPU 需要内存中某个数据的时候,如果寄存器中有这个数据,我们可以直接使用;
如果寄存器中没有这个数据,我们就要先查询 L1 缓存;L1 中没有,再查询 L2 缓存;
L2 中没有再查询 L3 缓存;L3 中没有,再去内存中拿。
L1-Cache:
- 在 CPU 中,相比寄存器,虽然它的位置距离 CPU 核心更远,但造价更低。
- 通常 L1-Cache 大小在几十 Kb 到几百 Kb 不等,读写速度在 2~4 个 CPU 时钟周期。
L2-Cache:
- 在 CPU 中,位置比 L1- 缓存距离 CPU 核心更远。
- 它的大小比 L1-Cache 更大,具体大小要看 CPU 型号,有 2M 的,也有更小或者更大的,速度在 10~20 个 CPU 周期。
L3-Cache:
在 CPU 中,位置比 L2- 缓存距离 CPU 核心更远。
大小通常比 L2-Cache 更大,读写速度在 20~60 个 CPU 周期。
L3 缓存大小也是看型号的,比如 i9 CPU 有 512KB L1 Cache,有 2MB L2 Cache,有16MB L3 Cache。
程序的执行过程
首先,CPU 读取 PC 指针指向的指令,将它导入指令寄存器。
具体来说,完成读取指令这件事情有 3 个步骤:
CPU 的控制单元操作地址总线指定需要访问的内存地址(简单理解,就是把 PC 指针中的值拷贝到地址总线中)
CPU 通知内存设备准备数据(内存设备准备好了,就通过数据总线将数据传送给 CPU)。
CPU 收到内存传来的数据后,将这个数据存入指令寄存器。
然后,CPU 分析指令寄存器中的指令,确定指令的类型和参数。
如果是计算类型的指令,那么就交给逻辑运算单元计算;如果是存储类型的指令,那么由控制单元执行。
PC 指针自增,并准备获取下一条指令。
比如在 32 位的机器上,指令是 32 位 4 个字节,需要 4 个内存地址存储,因此 PC 指针会自增 4。
CPU100%
top –c
,显示进程运行信息列表,找到最耗CPU
的进程。
按数字1,显示多核CPU信息。
键入P,进程按照CPU使用率排序。
按M按照内存占用进行排序。
top -Hp 【PID】
,显示一个进程的线程运行信息列表,找到最耗CPU的线程。
printf %x\n 【线程pid】
,转换多个线程数字为十六进制。
jstack 【进程PID】| grep 【线程转换后十六进制】-A10
, 使用JStack获取进程PID堆栈。
- 利用
Grep
定位线程ID,打印后续10行信息。
如果
VM Thread os_prio=0 tid=0x00007f871806e000 nid=0xa runnable
,第一个是线程名。
- 如果是
VM Thread
就是虚拟机GC回收线程了。
jstack 【进程PID】> 【文件】
,将JStack
堆栈信息存储到文件。
CPU负载情况
1 | top |
5:52:00
- 指的是当前时间
up 42:35
- 指的是机器已经运行了多长时间
1 user
- 指的是当前机器有1个用户在使用
load average: 0.15, 0.05, 0.01
- CPU在1分钟、5分钟、15分钟内的负载情况
假设是一个4核的CPU,此时如果你的CPU负载是0.15。
- 这就说明,4核CPU中连一个核都没用满,4核CPU基本都很空闲。
如果CPU负载是1,那说明4核CPU中有一个核已经被使用的比较繁忙了。
- 另外3个核还是比较空闲一些。
要是CPU负载是1.5,说明有一个核被使用繁忙,另外一个核也在使用。
- 但是没那么繁忙,还有2个核可能还是空闲的。
如果你的CPU负载是4,那说明4核CPU都被跑满了,如果你的CPU负载是6。
- 那说明4核CPU被繁忙的使用还不够处理当前的任务,很多进程可能一直在等待CPU去执行自己的任务。