操作系统对IO设备的操作,实质上就是对外设控制寄存器进行操作,并封装为统一接口(Linux中一切皆文件)。

终端IO

显示器

打开的相关文件,就是获得文件描述符,再通过文件描述符索引获得其PCB相关数组中的一项,之后得到文件的信息(inode)。

显示操作就是通过文件描述符得到之前的inode,根据文件类型(文件,字符设备...)选择如何处理,通过inode中的设备号,查表找到其处理函数写入缓冲区,最终通过在tty中的函数表通过汇编将缓冲区的内容写到显示器控制器。

具体见视频。

键盘

可以通过通过中断或者查询获取键码,根据键码查表获得处理函数(如查表得出键值放入缓冲区)。

下图是上面内容的简化图。

终端IO

磁盘

第一层抽象-生磁盘

我们会发现磁盘访问时间=写入控制器时间+寻道时间(找盘块和磁道)+旋转时间(找扇区)+传输时间

其中寻道时间最长要12~8ms,旋转时间次之要4ms,因此操作系统对扇区的排序是从里到外,从上到下的。同时因为寻找扇区的总时间远大于读写时间,那么何不一次多读几个扇区,这就是读写块(block),这提高了读写效率(但浪费了一定空间,空间换时间)。

这时用户要读取的就是块号,由操作系统转换成扇区号,通过扇区号算出真实的盘块、磁道、扇区。

Linux0.11中1个盘块可以是2个扇区刚刚好。

第二层抽象-多进程读写算法

这时单进程就可以操作磁盘了,那么多进程就要通过调度算法和请求缓冲队列来实现。

多进程读写磁盘

还是和之前一样一步一步推进算法:

  1. FCFS:使用FIFO最公平直观。

    ​ 缺点:磁头可能一直在反复移动,费时间。

  2. SSTF:Shortest-seek-time First根据排序请求根据扇区相近先服务。

    ​ 缺点:容易引起长时间不服务。

  3. SCAN:又称电梯算法,磁头要来回扫描同时排序请求,不断服务。如图:

    电梯算法

第三层抽象-文件

读写块对人来说还是不够方便,因此引入了文件。文件实质是一种字符序列(字符流),所以文件和磁盘的实质是建立字符流到盘块集合的映射关系

方式一:连续存放

因为使用的是连续存放,我们可以通过一个表(FCB),记录文件起始块、块数等;这样通过操作系统的计算就可以得出就可以得出块数。

这方法适合于读写文件,但不适合增删内容(就好像数组,词典)。

方式二:链式

第一种方法类似数组,那么是不是可以用链表来存储。这时FCB只要存储第一个块的号码,之后读取第一块,因为每一块的结尾都要下一块的号码,就可以访问文件。

这个方法和第一种的优点和缺点互换。

方式三:索引

结合上面两种,我们在FCB中也只存放一个块的号码,但这个块是特殊的,我们在这个块中建立文件和块的索引表

这就既可以读写,又可以增删。

文件索引

Linux0.11多重索引:

  • 小文件:直接用一个数组记录数据块块号(0级索引)

  • 大文件:数组前几个是记录数据块块号,后几个是记录索引块块号。如下图

    多重索引

在这时我们会发现Linux0.11中一切皆文件是一种抽象,文件都是通过文件的inode中的信息来识别去文件类型,在根据不同类型来对inode中的信息进行不同的操作。

第四层抽象-文件系统

在文件的基础上加入目录,将整个磁盘变成一颗树一样的结构,构成文件系统。

而目录的关键就是能根据路径名找到文件的FCB。

对于目录也是用上面文件的打开方式,每个目录也有FCB,类似文件,文件的数据块中存放的是一个一个目录项(文件名+对应的FCB位置)。如下图。

我们将FCB一起放在一个数组中,这样对应的位置就是数组的下标,方便索引。

文件目录

根目录要固定在磁盘的一个位置,通过根目录才能访问其他文件。

磁盘结构

上图是一块普通的硬盘,不是系统盘。

  • 引导块:引导用,这不是重点无需关心。
  • 超级块:记录该块和后面两个位图的信息(如大小)。
  • i节点位图:记录哪些inode空闲,哪些被占用,同时位图大小也就是能支持的文件数。
  • 盘块位图:提供硬盘的空间信息:哪些盘块是空闲的,哪些被使用,硬盘大小不同这个位图大小不同。
  • i节点:就是存放FCB数组。

位图:就是用二进制数字0表示使用,1为空闲。如:盘块位图:0011110011101就表示磁盘块2、3、4、5、8、9、10、12空闲。

到此就有了一个完整的文件系统。

使用完整流程