全文内容主要来自对课程《深入浅出计算机组成原理》的学习笔记。
40 | 理解内存(上)
虚拟内存地址究竟是怎么转换成物理内存地址?
简单页表
页表
(Page Table):虚拟内存里面的页,到物理内存里面的页的一一映射。
一个内存地址分成页号
(Directory)和偏移量
(Offset)。
一页内存在物理层面是连续的,一般大小是 4K 字节(4KB),需要 20 位的高位,12 位的低位。
内存地址转换步骤:
- 虚拟内存地址,切分成页号、偏移量;
- 从页表,查询对应的物理页号;
- 物理页号+偏移量,就是物理内存地址。
页表空间:
32位地址,高位有20,需要记录$2^{20}$个物理页号的映射,数组形势,一个页号4字节,总计大约4MB。看上去不多,但如每个进程有一份,就很大了。
多级页表
只需要去存用到的页之间的映射关系。
虚拟内存占用的地址空间,通常是两段连续的空间,多级页表
(Multi-Level Page Table)适用这类。
如上图,一个4级多级页表:
- 页号部分(20位)拆成4段(每段5位),对应各级页表索引;
- 每级索引对应的是下一级页表的位置;
- 最后的1级页表,继续用“页号 + 偏移量”来获取物理内存地址。
因为实际的虚拟内存空间通常是连续的,所以需要的 2、3 级页表很少。
看下空间大小:
- 每段索引5bit,1 级页表有$2^5=32$个条目映射,每个条目 4 字节(32bits=4Bytes),该 1 级页表总计 128 字节;
- 每个 1 级索引表映射 32 个 4KB 内存快,即 128Kb;
- 那么每个填满的2级索引表,对应 32 个 1 级索引表,总计 4MB 内存映射。
需要存的映射少了,空间节省了,但查询次数多了,一个典型的时间换空间的方案。
41 | 理解内存(下)
地址转换
是很高频,怎么解性能问题?- 数据、指令都在内存,怎么解内存安全问题?
加速地址转换:TLB
内存访问其实比 Cache 要慢,简单的内存转换,1次变4次。解法:加缓存。
这块缓存芯片我们称之为 TLB,全称是
地址变换高速缓冲
(Translation-Lookaside Buffer)。
和 CPU 里的高速缓存类似,用脏标记来实现“写回”等缓存管理策略。为了性能,整个内存转换过程也要由硬件来执行,封装为内存管理单元(MMU,Memory Management Unit)芯片
安全性与内存保护
可执行空间保护
一个进程使用的内存,指令部分设置成“可执行”的,数据等其他部分不给予“可执行”的权限。
地址空间布局随机化
核心问题:其他的人、进程、程序,会去修改掉特定进程的指令、数据,然后,让当前进程去执行这些指令和数据,造成破坏。
原进程内存空间是固定,容易获取指令位置。地址空间布局随机化之后,无法猜到指令的内存地址,随意修改只会让程序 crash 掉,不会执行危险代码。
一个经典的随机性应用:
用户名称+密码通过hash保护,但泄露太多加密的,容易通过彩虹表等方式推测结果。于是,在hash的时候全部加上盐值
(Salt),即使猜出来也无法使用。
42 | 总线
CPU 和内存、以及外部输入输出设备的通信,计算机是怎么完成的?
降低复杂性
假设计算机有 N 个不同的设备需要通信,并且是单独链接,那么复杂度就是 $N^2$。
优化的方案:不用单独通信,都是用一条公用线路,即总线
。
总线,就是一组线路,英文是 BUS,设计模式是事件总线
。
事件总线:
- 发布者:各模块触发对应的事件,并把事件对象发送到总线上;
- 监听者:各模块也注册到总线上,去监听事件,并根据对象类型或内容来决定是否要处理或者响应。
理解总线
双独立总线
(Dual Independent Bus,缩写为 DIB):
- 快速的
本地总线
(Local Bus); - 较慢的
前端总线
(Front-side Bus)。
CPU链接前端总线,即系统总线,与 I/O 桥接器相连,分别连接内存总线和 I/O 总线。
总线通常有三类线路:
数据线
(Data Bus),传输数据;地址线
(Address Bus),数据传输的位置,是内存的某个位置,还是某 I/O 设备。控制线
(Control Bus),用来控制对于总线的访问。
43 | 输入输出设备
接口和设备
输入输出设备一般2个部分:
- 接口(Interface);
- 实际的 I/O 设备(Actual I/O Device)。
三类寄存器(在设备的接口电路上):
- 状态寄存器(Status Register);
- 命令寄存器(Command Register);
- 数据寄存器(Data Register)。
控制 I/O 设备
设备接口上除了3类寄存器,还有控制电路来控制实际硬件。
- 数据寄存器(Data Register)。
CPU 向 I/O 设备写入需要传输的数据。实际上还有数据缓冲区。
- 命令寄存器(Command Register)。
CPU 发送打印命令给打印机。控制电路两个动作:状态寄存器把状态设置成 not-ready;操作打印机进行打印。
- 状态寄存器(Status Register)。
告诉 CPU 设备已在工作,其他数据和命令不能执行。直到完成,重新 ready 状态。
信号和地址
CPU 往总线上发送的命令具体是什么,才能和 I/O 接口上的设备通信呢?
答案是机器指令
。但 MIPS 并没有专门的 I/O 指令,实际上是使用内存地址
。
主内存会映射 I/O 设备的内存地址。CPU 通信时,往这些地址发送数据即可。这种叫内存映射 IO(Memory-Mapped I/O,简称 MMIO)。
Intel CPU 既支持 MMIO,还可以通过特定的指令,来支持端口映射 I/O(Port-Mapped I/O,简称 PMIO)。核心的区别,PMIO 里面访问的设备地址,不再是在内存地址空间里面,而是一个专门的端口(Port)。
44 | 理解IO_WAIT
并不是所有问题都能靠利用内存或者 CPU Cache 做一层缓存来解决。硬盘使用还是很多的,尤其是大数据场景,那么硬盘的 I/O 性能就很重要。
IO 性能、顺序访问和随机访问
硬盘厂商的性能报告:
- 响应时间;
- 数据传输率。
硬盘:
- HDD:机械硬盘,SATA 3.0 接口。
- SSD:固态硬盘,SATA 3.0 & PCI Express 接口。
SATA 3.0,带宽 6Gb/s,“b”是bit,则约768MB/s($6 \times 1024 / 8 = 768$)。但日常约 200 MS/s。
SSD 大约 500 MB/s,如果换成 PCI Express 约 2 GB/s。
响应时间,HDD 约十毫秒左右,SSD 约几十微妙,差异更大。但,无论哪个,貌似性能还可以,与实际经验不符合。
在顺序读写和随机读写的情况下,硬盘的性能是完全不同的
IOPS
:每秒输入输出操作的次数,去随机读取磁盘上某一个 4KB 大小的数据,一秒之内可以读取到多少数据。
SSD,随机读写约 40MB/s,即1万次 4KB 数据,即 IOPS。写入的话约90MB/s,IOPS 约2万。
而HDD,IOPS 大约为100。
定位 IO_WAIT
CPU 的主频通常在 2GHz 以上,也就是每秒可以做 20 亿次操作。即使一条读写指令,需要很多个时钟周期,硬盘完全跟不上。
通过 top 和 iostat 这些命令,一起来看看 CPU 到底有没有在等待 io 操作。
top 去看服务的负载,也就是 load average。也可以看 CPU 是否在等待 IO 操作完成。
以 %CPU 开头的行,有一个叫作 wa 的指标,这个指标就代表着 iowait
如果 iostat 很大,可以通过 iostat
查看实际硬盘读写。
其中, tps 指标,其实就对应着我们上面所说的硬盘的 IOPS 性能。而 kB_read/s 和 kB_wrtn/s 指标,就对应着我们的数据传输率的指标
用iotop
命令找哪一个进程是这些 I/O 读写的来源。
通过
stress -i 2
,模拟两个进程往硬盘写数据。top 的输出里面,CPU 就有大量的 sy 和 wa。通过 iostat,里面的 tps 很快就到了 4 万左右。iotop, I/O 占用都来自于 stress 产生的两个进程了。
45 | 机械硬盘
拆解机械硬盘
前面提过,机械硬盘 IOPS 大约100,这个的原理是什么?
机械硬盘三个组成部分:盘面、磁头和悬臂。
盘面
(Disk Platter):实际存储数据的,跟光盘类似,铝、玻璃或者陶瓷制成,上有磁性涂层来存储数据。中间有转轴控制转速,RPM(Rotations Per Minute)一般为7200,每秒则120转。
磁头
(Drive Head):从盘面读取数据传输给电路,一个硬盘会有多个堆叠的磁盘,每个磁盘正反面都会各有一个磁头。
悬臂
(Actutor Arm):链接磁头,用来定位磁头到磁道的。
磁盘会切分不同半径的同心环作为磁道
,每个磁道会分成多个扇区
,上下不同磁盘但平行的扇区称为一个柱面
。所以读取的数据的时候,悬臂需要控制磁头,移动到对应的磁道上,并且盘面需要转转道对应位置,如此磁头才能读取到指定的扇区/柱面。
硬盘上的随机访问:
平均延时
(Average Latency):盘面旋转,把几何扇区对准悬臂位置的时间。7200转/min 硬盘,即 240半圈/s,寻找指定扇区平均需要半圈距离,即 1s / 240 = 4.17ms。
平均寻道时间
(Average Seek Time):盘面旋转,悬臂定位到扇区的的时间。一般在 4-10ms
综上,随机访问时间约8-14ms,对应的 IOPS 为 125-70.
Partial Stroking
提升 HDD 硬盘效率的一个办法就是提高转速,这是很多厂商历史做的事情。
还有一种,空间换时间的方法,来提高硬盘的IOPS,就是 Partial Stroking
或者 Short Stroking
。
一般硬盘的寻道时间都比平均延时要长,如果消除这部分,可以有效提高 IOPS。那就是磁头仅仅用最外道的磁道,或者只用1/4磁道。
不过这是当年互联网蓬勃发展时,工程师们想要改善性能问题的有偿解法。