Basics of IO
IO 也是 memory 组成的一部分。我们使用 mapped memory 方式来访问 IO。
Characters of IO
IO 有三种:
- 只读:比如鼠标、键盘
- 只写:比如显示器
- 可读写:比如硬盘、网络接口
IO 的对象要么是人类(鼠标键盘显示器)、要么是机器(网络硬盘磁带)。
带宽是 IO 的重要指标。
Performance Metrics of IO
- 吞吐量(Throughput):吞吐量也可以分为两种情况。前者就是 bit/byte per second,后者则是 IOPS (i/o operations per second)
- 对于大容量存储(bulk storage)而言,带宽是瓶颈
- 对于流媒体来说,有时处理能力是瓶颈,因为需要传输大量的小视频块
- 反应时间:跟人交互的 IO 设备,response time 非常重要
Recap: Amdahl's Law
如果硬盘等 IO 的性能不提高,那么就会严重拖后腿。
Some Issues
我们有多种方法可以提高速度、增加寿命:
- 软件优化:虽然 average seek time 大可能会造成一定影响,但是,通过下面的方法,可以一定程度上使得 AST 不那么重要
- locality:尽量连续读
- OS scheduling:我们并不见到一块就去读一块,而是将需要读取的硬盘地址排序,使得 seek time 尽量低
- 使用更 smart 的磁盘控制器。
- 预取:可以使用更好的预取方式——预测接下来要用到的数据
- 我们还可以设计更好的空间分配算法,让磁盘的每一块能够更加均衡地被访问
- ……
硬盘控制器还要做些什么?
硬盘控制器还需要进行纠错编码(一般是 ECC)等等。
事实上,磁盘控制器是非常复杂的东西,设计难度并不小于一个 CPU
Categories of Storage Devices
对于磁盘,可以参考 DBMS: Storage。下面我们讲一讲 flash memory。
对于 NOR flash 而言,我们可以进行随机读写。缺点是储存密度不高,一般不超过 1MB。通常用于嵌入式设备的 IMem。
对于 NAND flash 而言,只能整块地读写,但是密度高,容量大,很容易达到 GB 级别,因此广泛用于 USB、移动硬盘等等。
NOR vs NAND: more details
对于 NOR 而言,写入的时候,如果是 1 改成 0,那么就可以直接改;如果是 0 改成 1,那么必须要整块擦除。
对于 NAND 而言,不管写些什么,都需要整块擦除。
而擦除会对其造成很大的破坏,一个 block 擦除 10000 次之后就不能用了。
Availability
除了 performance 以外,我们还需要考虑其稳定性。
电脑上每一个模块都有一个理想情况下的 behavior,如果实际执行情况与理想情况不符,就是 service interruption/failure。
Availability Metric
主要有三个概念:
- MTTF: Mean Time To Failure
- MTTR: Mean Time To Repair
- MTBF: Mean Time Between Failures
- MTBF = MTTR + MTTF
显而易见,availability = (time you can use) / (all time) = MTTF / MTBF = MTTF / (MTTF + MTTR)
如何提升 MTTF?
其中,fault tolerance 本质上就是通过冗余的方式来进行 fault avoidance。比如:
- 使用 ECC 纠正单比特错误
- 使用多 CPU 同时运行来纠正某一个 CPU 的错误(在太空中常用)
fault forecasting 有一个例子:如果服务器连续运行了一定的时间,就要进行重启,从而回收泄露的内存以及运行时 bug。
fault 都是谁引起的?
据统计(如下图),最多的 fault 还是人为引起的。因此要尽量自动化,少使用人工操作。
Disk Array
通过小磁盘 + 磁盘并行访问,我们可以做到成倍地加快磁盘速度。
问题就在于:通过现有的策略(RAID0,我们之后会讲到),
- 如果有一个磁盘坏了,那么所有硬盘上的数据都会损坏
- 同时,一个硬盘坏了,整个阵列都会无法使用。而增加磁盘的数量,将会成倍地减小 MTTF。
因此,我们需要通过冗余来减小这样的情况发生。
RAID: Redundant Array of Inexpensive Disks
为了均衡性能、储存效率和 MTTF,我们将 RAID 分成下面这么多的等级。
Level | Description | Minimum numbers of drives | Space efficiency | Fault tolerance | Fault isolation | Read performance | Write performance |
---|---|---|---|---|---|---|---|
RAID 0 | Block-level striping without parity or mirroring | 2 | 1 | None | Drive Firmware Only | n | n |
RAID 1 | Mirroring without parity or striping | 2 | 1/n | n − 1 drive failures | Drive Firmware or voting if n > 2 | n | 1 |
RAID 2 | Bit-level striping with Hamming code for error correction | 3 | \(1 - \frac1n \log_2(n+1)\) | One drive failure | Drive Firmware and Parity | Depends | Depends |
RAID 3 | Byte-level striping with dedicated parity | 3 | 1 − 1/n | One drive failure | Drive Firmware and Parity | n − 1 | n − 1 |
RAID 4 | Block-level striping with dedicated parity | 3 | 1 − 1/n | One drive failure | Drive Firmware and Parity | n − 1 | n − 1 |
RAID 5 | Block-level striping with distributed parity | 3 | 1 − 1/n | One drive failure | Drive Firmware and Parity | n | single sector: 1/4 |
RAID 6 | Block-level striping with double distributed parity | 4 | 1 − 2/n | Two drive failures | Drive Firmware and Parity | n | single sector: 1/6 |
如图,在实际中:
- RAID 0 被广泛使用,通常用于对速度、存储要求高,但是不太在乎 MTTF 的情况
- RAID 1 被用于非常在意数据安全的情况
- RAID 4 在某些网络应用中被使用
- 注意:RAID 3 本质上和 RAID 4 是一样的,只不过 RAID 4 更实际,以 block 为单元,而不是 byte
- RAID 5 被广泛使用,是比较均衡的选择
RAID 的一些概念
首先,衡量一种 RAID 效率的指标有两个:储存效率和读写效率。
- 储存效率:就是 \(\frac {\text{logical disk size}} {\text{physical disk size}}\)
- 读/写效率:主要就是支持多少并发读写
- Small Read/Write: 可以在满足约束条件的情况下,一次只读/写一行中某一个盘
- Large Read/Write: 在满足约束条件的情况下,一次必须读/写一行所有盘
- bit/byte/block 条带化:就是将 bit/byte/block 1 放到 disk 1,bit/byte/block 2 放到 disk 2,……,bit/byte/block n 放到 disk \(n \% \# \text{of disks}\)
RAID 3
如图,如果
- 只有一个盘损坏
- 我们知道损坏的是哪一个盘
那么就可以将这个盘进行恢复。
Parity Code vs Hamming Code
假如不知道损坏的是哪一个盘,还是老老实实用 hamming code 的 RAID 2 吧。
RAID 3 的不足
只能够 large write。而不能 small write。
- 这是因为 RAID 3 是 byte-条带化的,从而逻辑中的一块是分布在物理上的所有块上的。
- 因此,如果你希望写逻辑中的一块进入磁盘,那么必须在物理上将数据同时写入所有磁盘。
RAID 4
RAID 4 和 RAID 3 的差别是:
- RAID 4 是 block 条带化的,而 RAID 3 是 byte 条带化的。
- RAID 4 支持 small write。由于 \(P_\text{old} = \text{other tables} \oplus \text{old data of this table}\),因此,\(P_\text{new} = \text{other tables} \oplus \text{new data} = \text{other tables} \oplus \text{old data} \oplus \text{old data} \oplus \text{new data} = P_\text{old} \oplus \text{old data} \oplus \text{new data}\)。我们只需要通过该块的旧数据、新数据和奇偶校验块本身,就可以计算得出。
RAID 4 相比 RAID 3 的缺点
缺点很简单:假如说需要反复读某些块,而这些块在硬盘阵列上分布得不均匀。那么,就很容易导致硬盘之间的损耗不均匀(i.e. 某些硬盘比其他硬盘更早坏)。
RAID 4 的不足
虽然可以 small write,但是 write 的时候,奇偶校验盘和数据盘都会受影响,因此无法并行写两个盘。
RAID 5
RAID 5 就是在 RAID 4 的基础上,将奇偶校验盘均匀分布到了多个盘上。从而使得 small write 的并行性更好了(当然也无法保证一定可以并行)。
- 比如说:可以同时 small write D0, D0P 以及 D5, D5P 这四个磁盘,也就是逻辑上并行 small write D0, D5
RAID 6
RAID 6 就是采用两个奇偶校验盘,分别采用不同的奇偶校验算法,然后和 RAID 5 一样,也是均匀分布到多个盘上。这样就可以实现两个盘损坏也能恢复。
Bus
tl;dr
不同的总线设计,万变不离其宗。我们只需要记住:一条总线连到 CPU,其它所有设备挂在这个总线上。
如上图,这就是最简单的总线示意。总线可以通过分时复用来同时进行多个传输,因此共用总线不是问题。
最大的问题是:由于设备直接连在了总线上,而总线也用作 processor 和 memory 的通信,因此时钟频率必须很高。而 I/O 设备本身根本不需要那么高的频率(频率太高不省电)。如果需要跨时钟域的话,就会设计上有冗余。
因此,我们自然会想到采用的以下的设计:将跨时钟域的部分,作为 bus adapter,直接修在电路上。然后,每一个 bus adapter 都可以对应一个速率转换,我们只需要设计对应速率的硬件即可。
- 如图,采用更多的 bus adapters,可以让硬件有更多的速率选择
Handshake Protocol
对于跨时钟域传输,我们需要用到握手的协议。协议内容如下图所示:
简单来说:
- IO 设备给出 ReadReq 和内存地址。内存接收到了 ReadReq 之后,读取内存地址,然后返回 ack(表明我已经读完了,用不着了)
- IO 设备接收到 ack,就停止 ReadReq 和内存地址
- 内存发现 ReadReq 停止了,就知道 IO 设备已经接收到了 ack 信号,于是停止了 ack 信号
4 ~ 7 步同理。