操作系统对IO设备的操作,实质上就是对外设控制寄存器进行操作,并封装为统一接口(Linux中一切皆文件)。
终端IO
显示器
打开的相关文件,就是获得文件描述符,再通过文件描述符索引获得其PCB相关数组中的一项,之后得到文件的信息(inode)。
显示操作就是通过文件描述符得到之前的inode,根据文件类型(文件,字符设备...)选择如何处理,通过inode中的设备号,查表找到其处理函数写入缓冲区,最终通过在tty中的函数表通过汇编将缓冲区的内容写到显示器控制器。
具体见视频。
键盘
可以通过通过中断或者查询获取键码,根据键码查表获得处理函数(如查表得出键值放入缓冲区)。
下图是上面内容的简化图。
磁盘
第一层抽象-生磁盘
我们会发现磁盘访问时间=写入控制器时间+寻道时间(找盘块和磁道)+旋转时间(找扇区)+传输时间。
其中寻道时间最长要12~8ms,旋转时间次之要4ms,因此操作系统对扇区的排序是从里到外,从上到下的。同时因为寻找扇区的总时间远大于读写时间,那么何不一次多读几个扇区,这就是读写块(block),这提高了读写效率(但浪费了一定空间,空间换时间)。
这时用户要读取的就是块号,由操作系统转换成扇区号,通过扇区号算出真实的盘块、磁道、扇区。
Linux0.11中1个盘块可以是2个扇区刚刚好。
第二层抽象-多进程读写算法
这时单进程就可以操作磁盘了,那么多进程就要通过调度算法和请求缓冲队列来实现。
还是和之前一样一步一步推进算法:
-
FCFS:使用FIFO最公平直观。
缺点:磁头可能一直在反复移动,费时间。
-
SSTF:Shortest-seek-time First根据排序请求根据扇区相近先服务。
缺点:容易引起长时间不服务。
-
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空闲。
到此就有了一个完整的文件系统。