当第二个缓冲区也满了的时候,它也会把数据复制到用户空间中,然后第一个缓冲区用于接受新的字符。这种具有两个缓冲区的设计被称为 双缓冲(double buffering)。
还有一种缓冲形式是 循环缓冲(circular buffer)。它由一个内存区域和两个指针组成。一个指针指向下一个空闲字,新的数据可以放在此处。另外一个指针指向缓冲区中尚未删除数据的第一个字。在许多情况下,硬件会在添加新的数据时,移动第一个指针;而操作系统会在删除和处理无用数据时会移动第二个指针。两个指针到达顶部时就回到底部重新开始。
缓冲区对输出来说也很重要。对输出的描述和输入相似
缓冲技术应用广泛,但它也有缺点。如果数据被缓冲次数太多,会影响性能。考虑例如如下这种情况,
数据经过用户进程 -> 内核空间 -> 网络控制器,这里的网络控制器应该就相当于是 socket 缓冲区,然后发送到网络上,再到接收方的网络控制器 -> 接收方的内核缓冲 -> 接收方的用户缓冲,一条数据包被缓存了太多次,很容易降低性能。
错误处理在 I/O 中,出错是一种再正常不过的情况了。当出错发生时,操作系统必须尽可能处理这些错误。有一些错误是只有特定的设备才能处理,有一些是由框架进行处理,这些错误和特定的设备无关。
I/O 错误的一类是程序员编程错误,比如还没有打开文件前就读流,或者不关闭流导致内存溢出等等。这类问题由程序员处理;另外一类是实际的 I/O 错误,例如向一个磁盘坏块写入数据,无论怎么写都写入不了。这类问题由驱动程序处理,驱动程序处理不了交给硬件处理,这个我们上面也说过。
设备驱动程序统一接口我们在操作系统概述中说到,操作系统一个非常重要的功能就是屏蔽了硬件和软件的差异性,为硬件和软件提供了统一的标准,这个标准还体现在为设备驱动程序提供统一的接口,因为不同的硬件和厂商编写的设备驱动程序不同,所以如果为每个驱动程序都单独提供接口的话,这样没法搞,所以必须统一。
分配和释放一些设备例如打印机,它只能由一个进程来使用,这就需要操作系统根据实际情况判断是否能够对设备的请求进行检查,判断是否能够接受其他请求,一种比较简单直接的方式是在特殊文件上执行 open操作。如果设备不可用,那么直接 open 会导致失败。还有一种方式是不直接导致失败,而是让其阻塞,等到另外一个进程释放资源后,再进行 open 打开操作。这种方式就把选择权交给了用户,由用户判断是否应该等待。
设备无关的块注意:阻塞的实现有多种方式,有阻塞队列等
不同的磁盘会具有不同的扇区大小,但是软件不会关心扇区大小,只管存储就是了。一些字符设备可以一次一个字节的交付数据,而其他的设备则以较大的单位交付数据,这些差异也可以隐藏起来。
用户空间的 I/O 软件虽然大部分 I/O 软件都在内核结构中,但是还有一些在用户空间实现的 I/O 软件,凡事没有绝对。一些 I/O 软件和库过程在用户空间存在,然后以提供系统调用的方式实现。
作者:程序员cxuan
原文链接:https://juejin.im/post/5ed61b796fb9a047d3710eb1