操作系统I/O中的缓冲池
我们先来了解下操作系统缓冲技术的概念。为什么有缓冲这个东西呢?
缓冲,顾名思义就是起到一个调节的作用。我们知道cpu的处理速度是很快的,每秒钟百万条字节,而磁盘的I/O是
比较慢的,因为涉及到机械运动,性能差别很大。所以要有一个缓冲区用来缓和他们之间性能上的差异。磁盘I/O到
缓冲区中,然后缓冲区数据满了或者足够了之后通过总线放在cpu的高速缓冲区中,然后交给cpu处理,cpu处理完了
之后,回写到高速缓冲中。然后通过总线把数据放在缓冲区中。然后再回写到硬盘中。整个过程有两个甚至更多的缓
冲,为了就是解决性能上的差距。我们这里主要介绍下在磁盘I/O的缓冲技术中的缓冲池。
我们先来简单介绍除了缓冲池之外的其他缓冲技术。
1、单缓冲区:单个缓冲区是指要写到磁盘中的数据先放在缓冲区中,然后再把缓冲区的数据写到磁盘中。或者把磁盘
中的数据写在缓冲区中,然后由处理机取走。这种缓冲区由于要保证数据的正确性,而且缓冲区属于临界资源,需要进
行同步处理。只能有一个进程对数据进行写入或者读取。
2、双缓冲区:双缓冲区是指有两个缓冲区,当数据要写入磁盘的时候,首先写入第一缓冲区(a),第一缓冲区写满之后再
向第二个缓冲区(b)写入。当写入磁盘的时候,先把第一缓冲区(a)的数据写入到磁盘中。然后把第二个缓冲区(b)当作第一
个缓冲区(a)。再进行写入的时候写第二个缓冲区(a)。读操作类似。这样设计之后就能防止因缓冲区区域小导致数据写入
或者读取的不完整情况。提高并行性和设备的利用度。
3、循环缓冲区:
循环缓冲区是对单缓冲区的改良,这种缓冲区适合于读写速率差不多的情况,可以通过循环缓冲区来进行控制,这种机制
也可以提高设备的利用率。但是读写速率差距较大的时候,又不适合这种方式,这时候又可以引入多个缓冲区来适应速率
差别大的问题。
缓冲池技术:
缓冲池,说到底也是缓冲区,只不过这种缓冲区较其他缓冲区来说能更好的实现cpu并行和设备的利用率。
缓冲池里主要包括如下方面。用于对数据进行读写的公共区域:
1、 emq空缓冲队列:用于取出空缓冲区来进行读写等操作。
2、 inq输入队列:用来记录输入设备输入的数据,以便用户程序读取。
3、outq输出队列:用来记录用户程序输出到设备(显示屏或是文件等)的数据,以便用户程序读取。
先来分析下这个图的含义:
我们的缓冲区主要是有inq,outq,enq三个缓冲队列。其他四种hin,sout,sin,hout工作缓冲区是在这三种缓冲队列
上申请和取出缓冲区,用于数据的读取和存储。操作完成之后再把缓冲区放回到原来的队列。所以这四种缓冲区是抽象
出来的缓冲区,实际上还是在这三种缓冲队列上进行操作。下面我们来分析下四种操作对应是怎么实现的。
这四种工作缓冲区是在对应的队列中申请空间,所以我们有对对应的缓冲队列的读写两种操作。
Get_buf(封装Take_buf):从缓冲队列取出一个缓冲区的过程:
Void Get_buf(type) {
Wait(RS(type)); //type是指缓冲队列的类型,RS是用来线程同步信号量,初始值是缓冲区的大小。
Wait(MS(type)); //MS是用来互斥的信号量,保证缓冲队列的同一缓冲区只能被一个线程操作。
B(number)=Take_buf(type); //number是指缓冲队列对应的位置,这里是得到缓冲队列的缓冲区。得到之后可以用来读写数据,
Signal(MS(type)); //发送互斥信号,对缓冲队列中的空间操作完成。
}
Put_buf(封装Add_buf):从缓冲区插入对应缓冲队列的过程:
Void Put_buf(type,work_buf) {
Wait(MS(type)); //MS是用来互斥的信号量,保证缓冲队列的同一缓冲区只能被一个线程操作。
Add_buf(type,work_buf); //type是指缓冲队列的类型,work_buf代表工作缓冲区的类型。
Signal(MS(type)); //发送互斥信号,对缓冲队列中的空间操作完成。
Signal(RS(type)); //给对应类型的缓冲区发信号,代表返回缓冲队列的空间
}
上面对应的四种操作就可以通过调用这两个函数,给出不同参数实现对应的功能。
1、收容输入:把设备输入的数据放在缓冲区中。
①首先从空缓队列取出一个缓冲区当作hin工作缓冲区,hin=Get_buf(emq)。把数据装入到hin中
②把得到的hin缓冲区放在inq缓冲队列。inq=Put_buf(inq,hin)。供给用户程序利用
2、提取输入:把缓冲区的数据交给用户程序处理。
①把inq缓冲队列中的数据提取到sin工作缓冲区中,sin=Get_buf(inq)。供给用户提取数据。
②提取完数据之后,把sin的工作缓冲区放回到emq空缓冲队列中。emq=Put_buf(emq,sin)。
3:收容输出:把用户程序处理完的数据放在缓冲区中。
①先用工作缓冲区hout向空缓冲队列emq申请缓冲区,得到用户处理的数据,hout=Get_buf(emq)。
②hout的数据读取完毕之后,放在outq缓冲区中。Put_buf(outq,hout)供给外设读取。
4:提取输出:
①先把要输出到设备的缓冲区中的数据放在sout工作缓冲区中。sout=Get_buf(outq)。
②提取完sout的数据之后,把sout缓冲区放到空缓冲队列而emq中。emq=Put_buf(emq,sout)
有了缓冲池支撑缓冲,能够大大提高设备和cpu的利用率。这时候可以允许多个线程来处理数据。A线程送完了数据之
后,直接执行其他部分。B线程过来的时候带走应用程序处理的A线程的数据。放在对应的位置。多个设备也可同时对
缓冲区的不同位置进行读写,互不干扰。效率大大提高。