输入/输出
外部设备、接口部件、总线以及相应的管理软件统称为计算机的输入/输出系统,简称I/O系统
操作系统IO接口对于所有设备都应该是相同的,即设备无关性
基本功能
- 完成计算机内部二进制信息与外部多种信息形式间的交流
- 保证CPU能够正确选择输入输出设备并实现对其控制,传输大量数据、 避免数据出错
- 利用数据缓冲、选择合适的数据传送方式等,实现主机与外设间速度的匹配
特点
- 异步性:外围设备相对于处理机通常是异步工作的
- 实时性:当外围设备与处理机交互时,由于设备的类型不同,它们的工作步调是不同的,处理机必须按照不同设备所要求传送方式和传输速率不失时机地为设备提供服务,这就要求实时性控制
- 与设备无关性:各种外部设备必须根据其特点和要求选择一种标准接口和处理机进行连接,它们之间的差别必须由设备本身的控制器通过硬件和软件来填补;这样,处理机本身无须了解外设的具体细节,可以采用统一的硬件和软件对其管理
交换数据的过程
输入过程:
- CPU把一个地址值放在地址总线上,这一步将选择某一输入设备
- CPU等候输入设备的数据成为有效
- CPU从数据总线读入数据,并放在一个相应的寄存器中
输出过程:
- CPU把一个地址值放在地址总线上,选择输出设备
- CPU把数据放在数据总线上
- 输出设备认为数据有效,从而把数据取走
性能
按照主要完成的工作可以分为以下二类:
- 存储I/O
- 通信I/O
I/O系统的性能对CPU的性能有很大的影响,若两者的性能不匹配,I/O系统就有可能成为整个系统的瓶颈
评价参数
- 连接特性:哪些I/O设备可以和计算机系统相连接
- I/O系统的容量:I/O系统可以容纳的I/O设备数
- 响应时间:从用户输入命令开始,到得到结果所花费的时间,单位s
- 吞吐率:单位时间完成的I/O操作次数,单位IOP
I/O 硬件管理
I/O设备
- 块设备:以块为单位进行读写
- 字符设备:以字符为单位发送或接收字符流
设备控制器
IO设备由机械部件与电子部件构成 电子部件称为设备控制器或者适配器
控制器的任务: 将串行的比特流转换为字节块,并完成纠错工作
无条件IO
在程序的适当位置直接安排I/O指令,当程序执行到这些I/O指令时,CPU默认外设始终是准备就绪的(I/O总是准备好接收CPU的输出数据,或总是准备好向CPU输入数据),无需检查I/O的状态,就进行数据的传输;
硬件接口电路和软件控制程序都比较简单。输入时,必须确保CPU执行I/O指令读取数据时,外设已将数据准备好;输出时,必须确保外部设备的数据锁存器为空,即外设已将上次的数据取走,等待接收新的数据,否则会导致数据传送出错,但一般的外设难以满足这种要求
内存映射IO
控制器使用寄存器或者一块操作系统可以操作的RAM进行通信
给控制寄存器分配一个IO端口,所有IO端口形成IO端口空间吗,可以通过一条特殊的指令来来对控制寄存器进行读取或写入
IN REG, PORT ; CPU从指定IO端口读取数据到REG寄存器
OUT PORT, REG ; CPU写入数据
另外一种方式是将IO设备寄存器映射到内存上,对内存的读写都会通过总线信号映射到IO设备的寄存器。内存映射IO优点:
- 无需使用特殊指令读取IO设备寄存器,映射后能直接通过访问内存你的指令读写
- 不需要特殊保护机制阻止用户进程进行IO操作,操作系统只需要避免把映射的那块内存暴露在用户地址空间就能避免用户进程进行IO操作
- 可以应用于内存的指令也能应用于控制寄存器
缺点:
- 不能使用高速缓存,对IO映射内存做缓存的后果是灾难性的
计算机如何确定一个内存地址对应的是内存还是IO设备寄存器?现代的计算机大都包含高速内存总线,对内存的读写可以通过专用总线来进行,而对IO设备的读写则可以通过通用总线。
第二种设计是通过一个内存总线探查设备,该设备会对IO设备访问放行。
第三种设计则是对内存地址进行过滤,规定一个区域的地址为IO内存映射。
中断
中断: 是指CPU在正常运行程序时,由于内部/外部事件(或由程序)引起CPU中断正在运行的程序,而转到为中断事件服务的程序中去,服务完毕,再返回执行原程序的这一过程。中断具有 随机性
当设备发起一个中断信号,这个信号会被中断控制器芯片检测到。如果有设备同时发起中断,此时该设备中断不会被处理,不被处理的设备会不断发起信号,直至得到CPU的服务。
中断控制器在放置一个数字信号表明需要处理哪个设备,这个数字信号被称为中断向量,中断向量是一个服务过程的程序地址,CPU需要保存相关好相关信息,如PC,然后跳转到指定地址指向相应的服务过程。
中断的作用:
- 实现主机与外设之间的并行工作
- 故障处理
- 实时处理
由于现代CPU都会采用流水线技术,所以中断后一些指令执行了一部分,怎么确保这个状态是明确的
精确中断:机器停留在一个明确状态的中断
- 程序计数器保留在一个已知的地方
- 所指向的指令之前的所有指令已执行完毕
- 所执行的指令之后的所有指令都还未执行
- 所指向的指令的执行状态是已知的
不精确中断
中断的类型
- 内部中断
- 软件中断
- 异常
- 故障(Fault)
- 陷阱(Trap)
- 终止(Abort)
- 外部中断
- 可屏蔽中断INTR:由外设通过中断请求线向处理器申请而产生的中断,处理器可以用指令来屏蔽(禁止),即不响应它的中断请求
- 不可屏蔽中断NMI:由系统内部硬件引发的中断,优先级高于外部硬件中断,且不受中断允许标志位的影响,所以是不可屏蔽中断
中断的基本功能
- 中断请求信号保持与清除
- 中断源识别
中断号: 是系统分配给每个中断源的代号,以便识别和处理。中断号在中断处理过程中起到很重要的作用。
- 中断控制
中断触发方式: 是指外设以什么逻辑信号去申请中断,即边沿触发和电平触发两种方式
中断排队方式: 当系统有多个中断源时,就可能出现同时有几个中断源都申请中断,而处理器在一个时刻只能响应并处理一个中断请求;为此,要进行中断排队。处理器按“优先级高的先服务”的原则提供服务
当CPU正在处理某个中断时,会出现优先级更高的中断源申请中断;为了使更紧急的、级别更高的中断源及时得到服务,需要暂时打断(挂起)当前正在执行的中断服务程序,去处理级别更高的中断请求,处理完成后再返回被打断了的中断服务程序继续执行
但级别相同或级别低的中断源不能打断级别高的中断服务,这就是所谓的中断嵌套
可屏蔽中断可以进行中断嵌套。NMI不可以进行中断嵌套
- 中断屏蔽
处理器用指令来控制中断屏蔽触发器的状态,从而控制是否接受某个特殊外设的中断请求
处理器内部也有一个中断允许触发器,只有当其为“1”(即开中断),CPU才能响应外部中断
- 中断优先级
指CPU响应和处理中断请求的先后次序
硬件响应优先序:未被屏蔽的几个中断源同时提出申请时,CPU选择服务对象的顺序由硬件电路实现,用户不能修改
软件服务优先序:在各中断服务程序开头,用软件设置自己的中断屏蔽字,以此改变实际服务顺序
- 中断的处理
当CPU收到外设的中断请求后,如果当前一条指令已执行完,且允许中断,CPU进入中断响应周期,发出中断应答信号完成一个中断响应周期
读取中断源的中断号,完成中断申请与中断响应的握手过程
IO软件原理
IO软件的目标
- 设备独立性:面向接口编程
- 统一命名:设备的命名映射为一个简单的字符串或数字,不依赖于设备
- 错误处理:低层能处理的问题,就不要交给高层
- 同步与异步:大多数物理IO是异步的,只是read系统调用后,进程被挂起直至数据到达,如果IO是同步的,用户程序编写器就会很容易
- 缓冲:数据通常不能直接从源传输到目的地,所以需要缓冲区
程序控制IO
让CPU做全部工作 程序控制IO伪代码:
copy_from_user(buffer,p,count);
for(i = 0;i<count;i++){ // 循环写操作
while (*p_regs != READY){ // 检查IO设备是否就绪
*p_regd = p[i]; // 向IO设备写入数据
}
}
return_to_user();
这是一种早期计算机采用的输入/输出方式,数据在计算机和外设之间的传送全部靠计算机程序控制;计算机执行I/O指令时,先获取外设状态,并根据外设的状态决定下一步操作
何时对何设备进行输入输出操作完全受CPU控制,外围设备与CPU处于异步工作关系,数据的输入/输出都要经过CPU
优点: 计算机和外设之间能够同步,控制简单,硬件简单。
缺点: 要占用CPU的大量时间用来查询外设的状态。
设备状态字寄存器:用来标志设备的工作状态,以便接口对外部设备进行监视
中断驱动IO
程序控制IO浪费的地方在于如果IO设备不就绪,CPU的时间就浪费在无谓的循环的上面了。
中断驱动IO改进的地方在于当IO设备就绪,由IO设备主动产生一个中断,CPU在中断之前可以去调度其他进程,CPU接到外设的通知后暂停现行的工作,转入中断服务程序,和外设交换数据,等中断程序处理完毕后,再返回到被中断的原程序中继续以前被暂停的工作
优点: 节约CPU时间,实时性好。
缺点: 控制电路相对复杂,服务开销较大(现场和断点的保护)。
应用场合: 实时性要求高,且数据传输量又不大的场合。
使用DMA的IO
是一种完全由硬件执行的I/O交换方式,让DMA控制数据传输,而不必打扰CPU。本质上还是程序控制IO,只不过使用了DMA后CPU可以每次写入一整个缓冲区的数据,加入一个中间层 从CPU-设备 变成 CPU - DMA - 设备
- 突发模式:DMA直接让设备获得总线发起一连串传送,然后再释放总线
- 如果传送时间较长 有可能阻塞CPU或者其他设备
- 飞越模式: 让设备控制器将数据直接写到主存
当外设准备好后,通知DMA控制器,DMA控制器从CPU接管总线,并完成外设和内存之间的大量数据传输;传输完成后DMA控制器将总线控制权交还给CPU,整个数据交换的过程不需要CPU参与
设备控制器内部缓存区存在的原因
- 校验数据
- 总线可能忙
优点: 既有中断的优点,同时又降低了服务开销。
缺点: 控制电路更加复杂。
应用场合: 高速、大批量数据传输。
DMA控制器的两种工作状态
- 被动态(受控器):未取得总线控制权,受CPU的控制
- 主动态(主控器):接管并取得总线控制权,取代CPU而成为系统的主控者。
传输步骤
申请阶段:一个设备接口试图通过总线直接向另一个设备发送数据(一般是大批量的数据),它会先向CPU发送DMA请求信号
响应阶段:CPU收到DMA请求信号后,在当前的总线周期结束后,会按DMA信号的优先级和提出DMA请求的先后顺序响应DMA信号
数据传送阶段:CPU对某个设备接口响应DMA请求时,会让出总线控制权;于是在DMA控制器的管理下,外设和存储器直接进行数据交换,而不需CPU干预
传送结束阶段:数据传送完毕后,设备接口会向CPU发送DMA结束信号,交还总线控制权
操作类型
数据传送:把源地址的数据传输到目的地址去(存储器或I/O)
数据校验:不进行数据传输,只对数据块内部的每个字节进行某种校验;这种数据校验一般安排在读数据块之后,以便校验所读的数据是否有效
数据检索:不进行数据传输,只是在指定的内存区域内查找某个关键字节或某几个数据位是否存在
操作方式
- 单字节传输模式
- 块传输模式
- 请求参数模式
- 级联传输模式
零拷贝
对IO设备的读写,不将IO设备的数据先复制到内核空间,然后再复制到用户空间,以此获得更高的读写性能
这就是由 DMA 所完成的
用户态直接IO
硬件上的数据直接拷贝至了用户空间,不经过内核空间
mmap+write
实现内核缓冲区与应用程序内存的共享,省去了将数据从内核读缓冲区(read buffer)拷贝到用户缓冲区(user buffer)的过程,然而内核读缓冲区(read buffer)仍需将数据到内核写缓冲区(socket buffer)
主要的用处是提高 I/O 性能,特别是针对大文件。对于小文件,内存映射文件反而会导致碎片空间的浪费
sendfile
#include ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
简化通过网络在两个通道之间进行的数据传输过程。sendfile 系统调用的引入,不仅减少了 CPU 拷贝的次数,还减少了上下文切换的次数
sendfile 存在的问题是用户程序不能对数据进行修改
sendfile+ DMA gather copy
将内核空间(kernel space)的读缓冲区(read buffer)中对应的数据描述信息(内存地址、地址偏移量)记录到相应的网络缓冲区( socket buffer)中,由 DMA 根据内存地址、地址偏移量将数据批量地从读缓冲区(read buffer)拷贝到网卡设备中
同样也不能对数据进行修改
splice
splice(fd_in, off_in, fd_out, off_out, len, flags);
跟sendfile很像
写时复制
当多个进程共享同一块数据时,如果其中一个进程需要对这份数据进行修改,那么就需要将其拷贝到自己的进程地址空间中
缓冲区共享
每个进程都维护着一个缓冲区池,这个缓冲区池能被同时映射到用户空间(user space)和内核态(kernel space),内核和用户共享这个缓冲区池,这样就避免了一系列的拷贝操作
通道和I/O处理机
在复杂的计算机系统中,外围设备的台数一般比较多,设备的种类、工作方式和工作速度的差别很大,为了把对外围设备的管理工作从CPU中分离出来,采用通道或I/O处理机方式
通道是能够专门执行I/O指令的处理机,它可以实现对外围设备的统一管理,以及外设与主存之间的数据传输
I/O处理机是通道方式的进一步发展,它的结构更接近于一般处理机。
IO软件层次
中断处理程序
在响应一个特定中断的时候,内核会执行一个函数,该函数叫中断处理程序
隐藏中断的最好方式是将会引起中断的操作阻塞起来。但中断处理需要花费相当多的CPU指令。
设备驱动程序
每个链接到计算机上的IO设备都需要某些设备特定代码进行控制,称之为设备驱动程序
USB设备驱动通过堆栈式来达到不同的设备基于同样的技术效果。为了访问设备的硬件,大多数操作系统都要求驱动程序运行在内核中,这也是系统崩溃的一个源头之一。
可重入性:驱动必须意识到第一次调用完成之前第二次调用会到来
与设备无关的IO软件
- 设备驱动程序的统一接口
对于每种设备类型类型,操作系统定义一组驱动程序必须支持的函数,设备名可以使用设备类型+次版本号来编码,同样,对文件系统的保护规则也适用于设备。
- 缓冲
双缓冲:使用两个缓冲区交替使用,当一个满了之后直接复制到用户空间,另外一个接替上
环形缓冲:通过两个前后指针不断往前走实现一个逻辑上无限的缓冲区
- 错误报告
对于编程错误,如向一个输入设备发出了一个输出请求,操作系统直接返回错误码即可。
但对于实际的IO错误,应由驱动程序决定做什么,驱动程序解决不了,再向高层传递。但错误抛到高层,操作系统实际上除了返回一个错误码并失败外,并不存在其他多少事情可以做。
- 分配与释放专用设备
对于只允许有限数量进程使用的设备,操作系统必须对设备的使用请求进行检查,可以将得不到设备的进程调用失败掉。另外一种方式可以对得到设备的进程调用进行阻塞,而不是让其失败。
- 提供与设备无关的块大小
上层软件屏蔽掉不同设备的的块大小不一致
用户空间的IO软件
部分IO操作在用户空间完成,这是通过库过程来实现,也有通过假脱机的方式及守护进程的方式实现。
假脱机:IO设备通过一个文件来代表IO处理,用户进程直接处理这个文件来实现IO。
守护进程:用户进程通过将文件放置于某个目录下的,该目录称为假脱机目录,只有一个允许访问IO设备的进程,来读取这些文件进行操作,这个进程就是守护进程。
IO模型
- BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成
- NIO (Non-blocking/New I/O): NIO 是一种同步非阻塞的 I/O 模型,支持面向缓冲的,基于通道的 I/O 操作方法
- AIO (Asynchronous I/O):异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作
sequenceDiagram
title BIO
进程 ->> 操作系统: read
loop
操作系统 ->> 操作系统: 等待数据
end
操作系统 ->> 进程: 返回数据
sequenceDiagram
title 多路复用非阻塞IO
进程 ->> 操作系统: epoll
loop
操作系统 ->> 操作系统: 等待通道数据就绪
end
操作系统 ->> 进程: 返回数据就绪的通道
进程 -->> 通道1: 非阻塞read
通道1 -->> 进程: 有没数据都立即返回
进程 -->> 通道2: 非阻塞read
通道2 -->> 进程: 有没数据都立即返回
sequenceDiagram
title 异步IO
进程 ->> 操作系统: read
操作系统 ->> 进程: 立即返回
loop
操作系统 ->> 操作系统: 准备数据
end
操作系统 -->> 进程: 异步通知进程数据到达
阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程
Linux 默认的 read 与 write 系统调用就是阻塞的,但如果设置了 O_NONBLOCK O_NONBLOCK 标志,read 如果没有读到数据,则会简单返回 -EAGAIN,不像默认的 read 会等待直至有数据进来
同步与异步
同步和异步关注的是消息通信机制
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。
而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用
异步 IO 的实现有 Windows 的 IOCP、C++的 Boost asio、Linux 下的 aio
IO 多路复用
特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回
epoll select poll 都属于这种机制
/*
函数中定义了几个描述符集合,然后内核会关注这些描述符集合中
哪些描述符就绪了,然后返回已就绪的描述符个数,业务程序需要遍历所有的描述符集合
来找到可处理的描述符集合
*/
int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)
/*
和 select 函数相比,poll 函数的改进之处主要就在于
它允许一次监听超过 1024 个文件描述符
*/
int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
epoll 的优化点在于 epoll_wait 会返回就绪的描述符,而未就绪的描述符不返回
磁盘
磁盘驱动器不本身就包含一个微控制器,这允许磁盘驱动器发出一些诸如高速缓存、坏块重映射等高级命令
结构
- 盘面(Platter):一个磁盘有多个盘面
- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道
- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小
- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写)
- 制动手臂(Actuator arm):用于在磁道之间移动磁头
- 主轴(Spindle):使整个盘面转动
RAID
Redundant Array of Independent Disks,独立磁盘冗余阵列。通过将多个磁盘组合起来,使用RAID控制器代替磁盘驱动器以完成常规的操作。
- RAID0:将磁盘划分为多个条带,若读取多个条带的数据,可以实现并行IO,但对于每次读取一个条带的操作系统,性能不仅同普通磁盘,而且还有故障率更高的风险
- RAID1: 在RAID0的基础上整个数据复制一份,写性能比单个磁盘差,但读性能很高,并且拥有容错性,一旦一个磁盘挂掉,使用副本代替即可
- RAID2:工作在字的基础上,使用了汉明码来进行纠错,即使一个磁盘挂掉,也能恢复出正确数据
- RAID3:在2的基础上,使用奇偶校验码。如果磁盘挂掉,由于挂掉的磁盘位置已知,所以可以推算出丢失的数据位为0还是0 2和3的性能都不比单个磁盘好
- RAID4:重新使用了条带,使用一个驱动器专门存放前面4个条带的奇偶校验。但这种设计对小更新的性能很差,因为每次更新都需要更新校验和。
- RAID5:在4的基础上将校验和平均分布到各个磁盘,但如果某个磁盘挂掉,那就不好恢复了
- RAID6:在5的基础上使用额外的校验块。
磁盘格式化
低级格式化:对每个扇区设置前导码,ECC,由于前导码与ECC需要占用一定空间,所以可用磁盘容量总比宣传的小
↓
磁盘分区:0扇区包含主引导记录(MBR),MBR包含一些引导代码及分区表
↓
高级格式化:设置引导块、空闲存储管理、根目录、文件系统
系统的启动流程:BIOS运行->读入MBR并跳转->执行引导程序->找到操作系统内核载入内存执行
磁盘臂调度算法
读写磁盘块所需要的时间多少由以下三个因素决定:
- 旋转时间:主轴转动盘面,使得磁头移动到适当的扇区上需要的时间
- 寻道时间:制动手臂移动,使得磁头移动到适当的磁道上需要的时间
- 数据传输时间
调度算法:
- 先来先服务算法
- 按照磁盘请求的顺序进行调度
- 公平简单,但是没有对寻道做任何优化,平均寻道时间较长
- 最短寻道优先
- 优先调度距距离磁头最近的磁道
- 不够公平,如果一个请求距离当前磁头比较远,会出现饥饿现象
- 电梯算法
- 总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向
旋转时间与寻道时间十分影响性能,所以一次只读取一个或者两个扇区效率很低。现代的磁盘控制器都拥有高速缓存,每次读取多个扇区,并将其缓存。
错误处理
对于磁盘坏块的处理,可以在控制器或者在操作系统对他们进行处理。
在控制器中,处理的思想都是一样的,都是使用备用块来替代坏块。
而在操作系统级别进行处理,操作系统必须获的所有坏块列表,并将其进行重映射。
稳定存储器
保持磁盘的数据一致性。
- 稳定写:对每个磁盘驱动器轮流进行重复写操作
- 稳定读:如果有稳定写总是成功,并且一段时间内数据不会自己变坏的前提下,稳定写就总是成功的
- 崩溃恢复:当写的过程中发生崩溃,后续恢复的原则
- 两块都是好的且一样,什么也不做
- 一块有ECC错误,则好的覆盖掉有ECC错误的
- 两块都是好的但不一样,用第一块覆盖第二块
Partial Stroking
磁盘的两个时间:
- 平均延时:把盘面旋转,把几何扇区对准悬臂位置的时间
- 平均寻道时间:盘面旋转之后,悬臂定位到扇区的的时间
磁盘转速越高,平均延时就更低,如果不进行寻道,就可以剩下寻道的时间,同时越在磁盘外圈的数据访问越快,相同的角速度下,越外圈线速度越快,同样的时间扫过的扇区就越多
SSD
SLC 的芯片,可以擦除的次数大概在 10 万次,MLC 就在 1 万次左右,而 TLC 和 QLC 就只在几千次
磨损均衡
某些块写入比其他块更加频繁,这些块寿命会更短
TRIM命令
为了避免磨损均衡在搬运很多已经删除了的数据,现在的操作系统和 SSD 的主控芯片,都支持 TRIM 命令。这个命令可以在文件被删除的时候,让操作系统去通知 SSD 硬盘,对应的逻辑块已经标记成已删除了
写入放大
当 SSD 硬盘的存储空间被占用得越来越多,每一次写入新数据,可能不得不去进行垃圾回收,合并一些块里面的页,然后再擦除掉一些页
解决写入放大,需要在后台定时进行垃圾回收,在硬盘比较空闲的时候,就把搬运数据、擦除数据、留出空白的块的工作做完
最大化SSD使用效率
AeroSpike 这个 KV 数据库利用了 SSD的一些特性来加快读写速度:
- 尽可能去写一个较大的数据块,而不是频繁地去写很多小的数据块
- 读取数据的时候就可以读取小数据,不用担心擦写寿命问题
- 持续地进行磁盘碎片整理,一旦一个物理块里面的数据碎片超过 50%,就把这个物理块搬运压缩,然后进行数据擦除,确保磁盘始终有足够的空间可以写入,保 SSD 硬盘的写放大效应尽可能小
此外:
- 对于SSD而言,没了HDD的寻址代价,随机读写和顺序读写性能类似,就地更新不会获得任何 IOPS 优
- SSD对于空间局部性也没有优势,预取这种额外的数据访问,不仅会浪费 I/O 带宽,而且会不必要地磨损 SSD
- SSD可以执行并行小IO充分利用内部的并行性
时钟
时钟硬件
可编程时钟:石英晶体每次震荡会将递减计数器,计数器到0时会触发一个中断,软件可以自定义这个计数器来实现对时钟的编程。
- 一次完成模式:中断发生后等待软件显式启动
- 方波模式:中断一次之后,又重新开始循环
时钟软件
- 维护日时间
通过时钟来维护现在的时间,为了防止32位内存溢出,可以使用64位计数器,但代价过高。也可使用以秒为单位。
- 防止进程超时运行
时钟的每次中断就将时间片-1,当时间片为0,就得重新调度程序。
- 对CPU使用情况进行记账
每次滴答对进程表项的某个域+1来实现记录进程运行时间。
- 处理alarm系统调用
进程可以请求操作系统在一定的时间间隔后向它报警。
一个物理时钟为了模拟出多个时钟,可以通过维护一张表,每次时间发生更新就查找是否达到表中所需要的时刻,如果达到了,就进行触发。然后继续重复这个步骤。
- 为系统各部分提供监视定时器
检测死机之类的问题。如果操作系统可以定时清除计数器,当某个时刻计数器超过某个阈值,就可以确定已经死机了,此时软件介入处理。
- 完成信息收集
软定时器
IO有两种方式:中断和轮询。
现代CPU的中断开销是很大的,但轮询的响应时间又会比较高。
所以为了达到一个取舍,可以使用一个软定时器定时中断来进行IO。
外设
输入软件
键盘软件
- 原始模式:向用户提供原始的ASCII码序列
- 加工模式:由键盘驱动处理全部行内编辑,将编辑结果存储在缓冲区回车后写入
鼠标软件
鼠标发送的消息包含:$\Delta$x $\Delta$y 按钮 ,通常为3字节
鼠标单击与双击则是由GUI来进行区分的
输出软件
- 文本窗口
为了控制终端进行文字输出,程序使用了一种被称为“转义序列”的东西来控制终端。
- X窗口系统
- 图形用户界面
- 位图
- 字体
- 触摸屏
瘦客户机
电源管理
有两种方法减少电量消耗:
- 关闭不用的某些计算机硬件
- 使应用程序耗能更低
硬件问题
操作系统问题
- 显示器
- 硬盘
- CPU
- 内存
- 无线通信
- 热量管理
- 电源管理
- 驱动程序接口
应用程序问题