25

计算机组成原理笔记(四)

 4 years ago
source link: http://www.cnblogs.com/luozhiyun/p/12384340.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我的博客: https://www.luozhiyun.com/

内存

内存是五大组成部分里面的存储器,我们的指令和数据,都需要先加载到内存里面,才会被CPU拿去执行。

iYBJ7nB.png!web

我们的内存需要被分成固定大小的页(Page),然后再通过虚拟内存地址(Virtual Address)到物理内存地址(Physical Address)的地址转换(Address Translation),才能到达实际存放数据的物理内存位置。而我们的程序看到的内存地址,都是虚拟内存地址。

页表

想要把虚拟内存地址,映射到物理内存地址,最直观的办法,就是来建一张映射表。虚拟内存里面的页,到物理内存里面的页的一一映射。这个映射表,在计算机里面,就叫作页表(Page Table)。

页表这个地址转换的办法,会把一个内存地址分成页号(Directory)和偏移量(Offset)两个部分。

对于一个内存地址转换,其实就是这样三个步骤:

  1. 把虚拟内存地址,切分成页号和偏移量的组合;
  2. 从页表里面,查询出虚拟页号,对应的物理页号;
  3. 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址;
    ayYFNv2.jpg!web

多级页表(Multi-Level Page Table)

大部分进程所占用的内存是有限的,需要的页也自然是很有限的。我们只需要去存那些用到的页之间的映射关系就好了。

在整个进程的内存地址空间,通常是“两头实、中间空”。在程序运行的时候,内存地址从顶部往下,不断分配占用的栈的空间。而堆的空间,内存地址则是从底部往上,是不断分配占用的。

所以,在一个实际的程序进程里面,虚拟内存占用的地址空间,通常是两段连续的空间。

我们以一个4级的多级页表为例,来看一下。

eiiQNbI.jpg!web

对应的,一个进程会有一个4级页表。我们先通过4级页表索引,找到4级页表里面对应的条目(Entry)。这个条目里存放的是一张3级页表所在的位置。4级页面里面的每一个条目,都对应着一张3级页表,所以我们可能有多张3级页表。

找到对应这张3级页表之后,我们用3级索引去找到对应的3级索引的条目。3级索引的条目再会指向一个2级页表。同样的,2级页表里我们可以用2级索引指向一个1级页表。

而最后一层的1级页表里面的条目,对应的数据内容就是物理页号了。在拿到了物理页号之后,我们同样可以用“页号+偏移量”的方式,来获取最终的物理内存地址。

TLB加速地址转换

程序里面的每一个进程,都有一个属于自己的虚拟内存地址空间。我们可以通过地址转换来获得最终的实际物理地址。我们每一个指令都存放在内存里面,每一条数据都存放在内存里面。

“地址转换”是一个非常高频的动作,“地址转换”的性能就变得至关重要了。

多级页表让原本只需要访问一次内存的操作,变成了需要访问4次内存,才能找到物理页号。

程序所需要使用的指令,都顺序存放在虚拟内存里面。我们执行的指令,也是一条条顺序执行下去的。

于是,计算机工程师们专门在CPU里放了一块缓存芯片。这块缓存芯片我们称之为TLB,全称是地址变换高速缓冲(Translation-Lookaside Buffer)。这块缓存存放了之前已经进行过地址转换的查询结果。这样,当同样的虚拟地址需要进行地址转换的时候,我们可以直接在TLB里面查询结果,而不需要多次访问内存来完成一次转换。

TLB和我们前面讲的CPU的高速缓存类似,可以分成指令的TLB和数据的TLB,也就是ITLB和DTLB。

RZNfqym.jpg!web

为了性能,我们整个内存转换过程也要由硬件来执行。在CPU芯片里面,我们封装了内存管理单元(MMU,Memory Management Unit)芯片,用来完成地址转换。和TLB的访问和交互,都是由这个MMU控制的。

I/O

我们先来看一个固态硬盘的Benchmark图:

2INzae3.png!web

“4K”的指标就是我们的程序,去随机读取磁盘上某一个4KB大小的数据,一秒之内可以读取到多少数据。

我们拿这个40MB/s和一次读取4KB的数据算一下。 40MB / 4KB = 10,000 也就是说,一秒之内,这块SSD硬盘可以随机读取1万次的4KB的数据。如果是写入的话呢,会更多一些,90MB /4KB 差不多是2万多次。

这个每秒读写的次数,我们称之为IOPS,也就是每秒输入输出操作的次数。

DTR(Data Transfer Rate,数据传输率)

我们在实际的应用开发当中,对于数据的访问,更多的是随机读写,而不是顺序读写。

诊断 I/O瓶颈

首先看一下CPU有没有在等待io操作。

# top

top - 06:26:30 up 4 days, 53 min,  1 user,  load average: 0.79, 0.69, 0.65
Tasks: 204 total,   1 running, 203 sleeping,   0 stopped,   0 zombie
%Cpu(s): 20.0 us,  1.7 sy,  0.0 ni, 77.7 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
KiB Mem:   7679792 total,  6646248 used,  1033544 free,   251688 buffers
KiB Swap:        0 total,        0 used,        0 free.  4115536 cached Mem

wa的指标,这个指标就代表着iowait,也就是CPU等待IO完成操作花费的时间占CPU的百分比。

如果iowait很大,那么就可以去看看实际的I/O操作情况是什么样的。使用iostat,就能够看到实际的硬盘读写情况。

$ iostat

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          17.02    0.01    2.18    0.04    0.00   80.76
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               1.81         2.02        30.87     706768   10777408

tps指标,其实就对应着我们上面所说的硬盘的IOPS性能。而kB_read/s和kB_wrtn/s指标,就对应着我们的数据传输率的指标。

使用iotop找出到底是哪一个进程是这些I/O读写的来源。

$ iotop

Total DISK READ :       0.00 B/s | Total DISK WRITE :      15.75 K/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:      35.44 K/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND                                             
  104 be/3 root        0.00 B/s    7.88 K/s  0.00 %  0.18 % [jbd2/sda1-8]
  383 be/4 root        0.00 B/s    3.94 K/s  0.00 %  0.00 % rsyslogd -n [rs:main Q:Reg]
 1514 be/4 www-data    0.00 B/s    3.94 K/s  0.00 %  0.00 % nginx: worker process

硬盘

机械硬盘

uaEBvyR.jpg!web

一块机械硬盘是由盘面、磁头和悬臂三个部件组成的。

首先,自然是盘面(Disk Platter)。盘面其实就是我们实际存储数据的盘片。

我们的硬盘有5400转的、7200转的,乃至10000转的。这个多少多少转,指的就是盘面中间电机控制的转轴的旋转速度,英文单位叫RPM,也就是每分钟的旋转圈数(Rotations Per Minute)。

磁头:数据并不能直接从盘面传输到总线上,而是通过磁头,从盘面上读取到,然后再通过电路信号传输给控制电路、接口,再到总线上的。通常,我们的一个盘面上会有两个磁头,分别在盘面的正反面。

悬臂链接在磁头上,并且在一定范围内会去把磁头定位到盘面的某个特定的磁道(Track)上。

一个盘面通常是圆形的,由很多个同心圆组成,每一个同心圆都是一个磁道。每个磁道都有自己的一个编号。

一个磁道,会分成一个一个扇区(Sector)。上下平行的一个一个盘面的相同扇区呢,我们叫作一个柱面(Cylinder)。

读取数据,其实就是两个步骤。

  1. 把盘面旋转到某一个位置。在这个位置上,我们的悬臂可以定位到整个盘面的某一个子区间。
  2. 把我们的悬臂移动到特定磁道的特定扇区,也就在这个“几何扇区”里面,找到我们实际的扇区。找到之后,我们的磁头会落下,就可以读取到正对着扇区的数据。

进行一次硬盘上的随机访问,需要的时间由两个部分组成。

第一个部分,叫作平均延时(Average Latency)。这个时间,其实就是把我们的盘面旋转,把几何扇区对准悬臂位置的时间。这个时间很容易计算,它其实就和我们机械硬盘的转速相关。

随机情况下,平均找到一个几何扇区,我们需要旋转半圈盘面。上面7200转的硬盘,那么一秒里面,就可以旋转240个半圈。那么,这个平均延时就是:1s / 240 = 4.17ms

第二个部分,叫作平均寻道时间(Average Seek Time),也就是在盘面选转之后,我们的悬臂定位到扇区的的时间。我们现在用的HDD硬盘的平均寻道时间一般在4-10ms。

SSD硬盘

现在新的大容量SSD硬盘是由很多个裸片(Die)叠在一起的,就好像我们的机械硬盘把很多个盘面(Platter)叠放再一起一样,这样可以在同样的空间下放下更多的容量。

vERve2J.jpg!web

一张裸片上可以放多个 平面 (Plane),一般一个平面上的存储容量大概在GB级别。一个平面上面,会划分成很多个块(Block),一般一个块(Block)的存储大小, 通常几百KB到几MB大小。一个块里面,还会区分很多个页(Page),就和我们内存里面的页一样,一个页的大小通常是4KB。

对于SSD硬盘来说,数据的 写入 叫作Program。写入不能像机械硬盘一样,通过 覆写 (Overwrite)来进行的,而是要先去 擦除 (Erase),然后再写入。

SSD的读取和写入的基本单位,不是一个比特(bit)或者一个字节(byte),而是一个 (Page)。SSD的擦除单位必须按照 来擦除。

SSD的使用寿命,其实是每一个块(Block)的擦除的次数。

SLC的芯片,可以擦除的次数大概在10万次,MLC就在1万次左右,而TLC和QLC就只在几千次了。

SSD读写的生命周期

白色代表这个页从来没有写入过数据,绿色代表里面写入的是有效的数据,红色代表里面的数据,在我们的操作系统看来已经是删除的了。

VbIJFrJ.jpg!web

一开始,所有块的每一个页都是白色的。随着我们开始往里面写数据,里面的有些页就变成了绿色。

然后,因为我们删除了硬盘上的一些文件,所以有些页变成了红色。但是这些红色的页,并不能再次写入数据。因为SSD硬盘不能单独擦除一个页,必须一次性擦除整个块,所以新的数据,我们只能往后面的白色的页里面写。这些散落在各个绿色空间里面的红色空洞,就好像硬盘碎片。

如果有哪一个块的数据一次性全部被标红了,那我们就可以把整个块进行擦除。它就又会变成白色,可以重新一页一页往里面写数据。

在快要没有白色的空页去写入数据的时候,SSD会做一次类似于Windows里面“磁盘碎片整理”或者Java里面的“内存垃圾回收”工作。找一个红色空洞最多的块,把里面的绿色数据,挪到另一个块里面去,然后把整个块擦除,变成白色,可以重新写入数据。

DMA

为什么要发明DMA技术?

就目前而言I/O速度如何提升,比起CPU,总还是太慢。如果我们对于I/O的操作,都是由CPU发出对应的指令,然后等待I/O设备完成操作之后返回,那CPU有大量的时间其实都是在等待I/O设备完成操作。

但是,这个CPU的等待,在很多时候,其实并没有太多的实际意义。我们对于I/O设备的大量操作,其实都只是把内存里面的数据,传输到I/O设备而已。

因此,计算机工程师们,就发明了DMA技术,也就是 直接内存访问 (Direct Memory Access)技术,来减少CPU等待的时间。

DMA有什么用?

本质上,DMA技术就是我们在主板上放一块独立的芯片。在进行内存和I/O设备的数据传输的时候,我们不再通过CPU来控制数据传输,而直接通过 DMA控制器 (DMA Controller,简称DMAC)。

当传输大量数据的时候,DMAC可以等数据到齐了,再发送信号,给到CPU去处理,而不是让CPU在那里忙等待。

DMAC是怎么控制数据传输的?

DMAC其实也是一个特殊的I/O设备,它和CPU以及其他I/O设备一样,通过连接到总线来进行实际的数据传输。总线上的设备呢,其实有两种类型。一种我们称之为 主设备 (Master),另外一种,我们称之为 从设备 (Slave)。

想要主动发起数据传输,必须要是一个主设备才可以,CPU就是主设备。而我们从设备(比如硬盘)只能接受数据传输。

DMAC它既是一个主设备,又是一个从设备。对于CPU来说,它是一个从设备;对于硬盘这样的IO设备来说呢,它又变成了一个主设备。

我们下面看一张图:

IVf63ai.jpg!web

  1. 首先,CPU还是作为一个主设备,向DMAC设备发起请求。这个请求,其实就是在DMAC里面修改配置寄存器。
  2. CPU修改DMAC的配置的时候,会告诉DMAC这样几个信息:
    • 首先是源地址的初始值以及传输时候的地址增减方式。

      所谓源地址,就是数据要从哪里传输过来。如果我们要从内存里面写入数据到硬盘上,那么就是要读取的数据在内存里面的地址。

    • 其次是 目标地址初始值和传输时候的地址增减方式
    • 第三个是 要传输的数据长度
  3. 设置完这些信息之后,DMAC就会变成一个空闲的状态(Idle)。
  4. 如果我们要从硬盘上往内存里面加载数据,这个时候,硬盘就会向DMAC发起一个数据传输请求。这个请求并不是通过总线,而是通过一个额外的连线。
  5. 然后,我们的DMAC需要再通过一个额外的连线响应这个申请。
  6. 于是,DMAC这个芯片,就向硬盘的接口发起要总线读的传输请求。数据就从硬盘里面,读到了DMAC的控制器里面。
  7. 然后,DMAC再向我们的内存发起总线写的数据传输请求,把数据写入到内存里面。
  8. DMAC会反复进行上面第6、7步的操作,直到DMAC的寄存器里面设置的数据长度传输完成。
  9. 数据传输完成之后,DMAC重新回到第3步的空闲状态。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK