目录
硬件缓冲和软件缓冲
(1) 硬件缓冲区:配置在设备中,具有专门的用途。
- 对处理机透明,不需要处理机的直接管理,不会影响系统性能。
(2) 软件缓冲区:是内存空间的一部分。
- 其目的是为了弥补硬件缓冲区的不足。
- 因为并非所有外设都拥有足够的硬件缓冲区。
- 操作系统中介绍的缓冲区都是指软件缓冲区。
5.7.1 缓冲的引入
- 缓和 CPU 与 I/O 设备间速度不匹配的矛盾
- 减少对 CPU 的中断频率,放宽对 CPU 中断响应时间的限制
- 解决数据粒度不匹配的问题
- 提高 CPU 和 I/O 设备之间的并行性
如下图所示,我们可以看出缓冲带来的好处,以及如何利用缓冲寄存器实现缓冲:
5.7.2 单缓冲和双缓冲
1、单缓冲(Single Buffer)
单缓冲:一个缓冲区将被 CPU 和外设轮流使用,一方处理完毕后接着等待另一方处理。
𝑪和𝑻可并行,𝑴和𝑪、𝑴和𝑻不能并行,因此处理一块数据时间:𝒎𝒂𝒙(𝑪,𝑻)+𝑴
由于只有一个缓冲区,因此 CPU 和外设在处理时都是独占缓冲区,故输入(T)和传送(M)不能同时进行。
2、双缓冲(Double Buffer)
双缓冲:设置两个缓冲区,CPU 和外设都可以连续处理而无需等待对方。
- 要求 CPU 和外设的速度相近。
- 效率有所提高,且进一步平滑了传输峰值。
- 系统处理一块数据的时间可以粗略地认为是:𝒎𝒂𝒙(𝑪,𝑻)
- 收发可双向同时传送,𝑴和𝑻可并行。
3、双机通信时缓冲区的设置
5.7.3 循环缓冲
当用户进程处理数据的速度较快、外部设备处理数据的速度较慢,或者用户进程阵发性输入/输出的数据较多时,必须考虑增加缓冲区的数量以改善系统性能,这就是多缓冲区方式。
循环缓冲:多个 I/O 缓冲区常常被组织成一个环形队列。
实质上,双缓冲可以看作是循环缓冲的一个特例。
1、循环缓冲的组成
(1) 多个缓冲区。
(2) 多个指针。
可为缓冲区设置三个指针:
- 指示计算进程下一个可用缓冲区 G 的指针 Nextg
- 指示输入进程下一个可用空缓冲区 R 的指针 Nexti
- 指示计算进程正在使用的缓冲区 C 的指针 Current
2、循环缓冲区的使用
计算进程和输入进程可利用下述两个过程来使用循环缓冲区。
(1) Getbuf 过程
① 当输入进程向空缓冲区写入数据时,由该过程将指针 Nexti 所指示的缓冲区提供给输入进程使用,同时将 Nexti 指针移向下一个 R 缓冲区。
② 当计算进程从满缓冲区读出数据时,由该过程将指针 Nextg 所指示的缓冲区提供给计算进程使用,把它改为现行工作缓冲区,并令 Current 指针指向该缓冲区的第一个单元,同时将 Nextg 指针移向下一个 G 缓冲区。
(2) Releasebuf 过程
① 当输入进程把缓冲区写满时,调用 Releasebuf 过程,将该缓冲区释放并改为 G 缓冲区。
② 当计算进程把缓冲区读完时,调用 Releasebuf 过程,将该缓冲区释放并改为 R 缓冲区。此外,还要把该缓冲区由 C 缓冲区改为 R 缓冲区。
3、进程同步
(1) Nexti 追赶上 Nextg
输入速度大于计算速度,缓冲区满,输入进程阻塞,此情况称为系统受计算限制。
(2) Nextg 追赶上 Nexti
输入速度低于计算速度,缓冲区空,计算进程阻塞,此情况称为系统受 I/O 限制。
5.7.4 缓冲池
上述三种缓冲区的组织形式仅适用于某种特定的 I/O 进程和计算进程,属于专用缓冲。当系统中的设备很多时,将会有许多这样的循环缓冲区,消耗大量的内存空间,而且其利用率也不高。为了提高缓冲区的利用率,可以采用 公共缓冲池 技术,其中的缓冲区可为多个设备和进程服务。
两种缓冲池:
- 用于块型设备的缓冲池:缓冲区较大,其长度通常与外部设备物理块的长度相同;
- 用于字符型设备的缓冲池:缓冲区较小,其长度通常为 8 个字节、16 个字节等。
若单个缓冲区的容量太大会造成资源浪费,而太小的缓冲区则会增加系统管理开销。
1、缓冲池的组成
公用缓冲池,含有以下三种类型的缓冲区:
- 空缓冲区:输入数据和输出数据都能装
- (满缓冲区)装满输入数据的缓冲区
- (满缓冲区)装满输出数据的缓冲区
2、缓冲区队列
为了管理上的方便,可将相同类型的缓冲区链成一个队列,于是可形成以下三个队列:
- 空缓冲队列 emq:这是由空缓冲区所链成的队列。
- 输入队列 inq:这是由装满输入数据的缓冲区所链成的队列。
- 输出队列 outq:这是由装满输出数据的缓冲区所链成的队列。
队列是由缓冲区链接而成的,从队列中摘下的就是缓冲区。
3、Getbuf 过程和 Putbuf 过程
对缓冲区进行操作的两个过程:
// type指队列类型:emq、inq、outq
Getbuf(type)
// number指缓冲区的编号
Putbuf(type, number)
对队列进行操作的两个过程:
// 取type类型队列的首缓冲区
Takebuf(type)
// 将number所指的缓冲区加到队列的队尾
Addbuf(type, number)
信号量设置:
- MS:互斥信号量,每个队列一个
- RS:资源信号量,每个队列一个
MS(type) 代表类型为 type 的队列的互斥信号量,RS(type) 代表类型为 type 的队列的资源信号量。
Getbuf 过程和 Putbuf 过程:
Procedure Getbuf(type)
begin
Wait(RS(type)); //资源信号量
Wait(MS(type)); //互斥信号量
//从队首摘下一个缓冲区
B(number) := Takebuf(type);
Signal(MS(type)); //互斥信号量
end
Procedure Putbuf(type, number)
begin
Wait(MS(type)); //互斥信号量
//将指定缓冲区挂在队列的队尾
Addbuf(type, number);
Signal(MS(type)); //互斥信号量
Signal(RS(type)); //资源信号量
end
4、缓冲区的工作方式
(1) 收容输入 hin
输入进程可调用 Getbuf(emp) 过程,从空缓冲区队列 emq 的队首摘下一空缓冲区,把它作为收容输入工作缓冲区 hin 。然后,把数据输入其中,装满后再调用 Putbuf(inq, hin) 过程,将它挂在输入队列 inq 末尾。
(2) 提取输入 sin
计算进程可调用 Getbuf(inq) 过程,从输入队列 inq 的队首摘下一满缓冲区,作为提取输入工作缓冲区 sin 。计算进程从中提取完数据后,再调用 Putbuf(emq, sin) 过程,将它挂到空缓冲队列 emq 末尾。
(3) 收容输出 hout
输入进程可调用 Getbuf(emp) 过程,从空缓冲区队列 emq 的队首摘下一空缓冲区,把它作为收容输出工作缓冲区 hout 。然后,把数据输入其中,装满后再调用 Putbuf(outq, hout) 过程,将它挂在输出队列 outq 末尾。
(4) 提取输出 sout
输出进程可调用 Getbuf(outq) 过程,从输出队列 outq 的队首摘下一满缓冲区,作为提取输出工作缓冲区 sout 。在数据提取完后,再调用 Putbuf(emq, sout) 过程,将它挂在空缓冲队列末尾。