【linux0.12】从open系统调用到柱面磁头扇区上篇-----原理讲解

13 篇文章 0 订阅
10 篇文章 0 订阅

概述

  上篇只讲原理,不涉及代码,下篇将从代码层面进行讲解
  虽然标题是从系统调用到柱面磁头扇区,但我们知道,操作系统是联系硬件和用户的一个中间层,为用户操作硬件提供了方便和高效,操作系统的任何操作最终都会落实到硬件(这里是磁盘)上,所以我选择了从硬件开始讲起,看操作系统是如何一步一步把磁盘抽象为最终的文件系统的,反过来也就是用户如何通过文件系统来达到操作磁盘的目的。

磁盘

先来看一下磁盘的物理结构
  通过下方的图片,我们可以知道磁盘是由盘面,磁头,磁臂组成的,其他的磁道,扇区,柱面是人为的划分,物理上只由盘面磁头磁臂组成。
在这里插入图片描述

磁道 扇区 柱面

  可以看到磁盘是一个圆盘,由许许多多的同心圆组成,那么这每一个同心圆就是一个磁道。
在这里插入图片描述

  从中心转轴向外划分直线,可以把圆盘划分为一个个的圆弧,那么这每个圆弧就是一个扇区,典型的扇区大小是512个字节(最内侧磁道的扇区面积最小,因此数据密度最大)。在这里插入图片描述

  可以看到磁盘是由很多盘面组成,所有盘面上的同一个同心圆(磁道)组成了一个柱面图,那么这就是柱面的概念,为什么要这么麻烦的弄出一个柱面的概念呢,稍后我们就会讲到,实际上这是因为磁臂的移动时间是很长的,为了尽量减少磁臂的移动时间,要在不移动磁臂的情况下读写磁盘,那么不移动磁臂时相同的磁道和扇区就组成了一个个的柱面,从而引出了柱面的概念。
在这里插入图片描述

磁盘如何工作

  我们已经知道,磁盘是由磁臂、磁头、柱面、扇区组成的,而且磁盘的读写是以扇区为单位,那么硬件被告知柱面磁头扇区这几个硬件的位置时,就开始进行电生磁的读入内存或者磁生电的写到磁盘。

从柱面磁头扇区到盘块号

  经过上面的讨论,我们知道了用户需要清楚柱面磁头扇区的位置才能对硬件进行操控,这样对使用磁盘极其的不方便,那么有没有什么方法来简化这种操作呢。答案是有的,那就是把零散的分布在各个盘面上的扇区按某种顺序对它们进行编码,利用这个编码来对它们进行操作,这个编码就是盘块号。
  ok,那需要按怎样的顺序对它们进行编码才能做到快捷高效呢,注意我们的目的不仅是方便,而且还要尽可能的做到快捷和高效,那怎样才能做到高效和省时呢,那我们就需要知道时间花在哪里,请看这个公式:
  磁盘访问时间(约10ms)=写入控制器时间+寻道时间(8-12ms)+旋转时间(半周4ms)+传输时间(0.3ms)
  可以看到,时间主要花在了寻道时间,也就是磁臂的移动时间,那为了省时,就需要在尽量少移动磁臂的情况下对扇区进行读写,这包含了两种方法,一种是尽量不移动磁臂进行读写,一种是一次尽量读写多个扇区。一次读写扇区数越多,那么相对效率就越高,如:磁盘访问时间为10ms,每次读写1k,碎片0.5k,读写速度为100k/s。每次读写1M,碎片0.5M,读写速度约40M/s,基于这个原理,linux0.12中是把每两个扇区作为一个盘块号进行编码的。不移动磁臂和磁头的话,相同的磁道在所有盘面上就构成了一个柱面,也就是我们柱面的概念。所以最终的编码方式是:先按柱面,把一个盘面上的一个磁道按顺序排列,因为此时读其他柱面需要旋转磁臂,一个柱面由多个磁头,所以接下来读相同柱面上的下一个磁道,这样对整个柱面后排列完成后再对下一个柱面进行排列,对下一个柱面进行类似的编码,直至对磁盘全部编码完毕。
  那么由上面对扇区编码完毕后,具体怎样计算呢。假设我们的磁盘要查找第c个柱面上第H个磁道上第S个扇区,那对应的盘块号就等于:
  C*(Heads* Sectors)+H*Sectors+S=block
  上方公式的Heads和Sectors分别代表每个柱面上的磁头数和每个磁道上的扇区数,是固定的数据,是在买磁盘的时候就已经确定好的常量。
  有了这个公式,那我们轻而易举的可以根据block号来算出对应的柱面磁头扇区号,所以现在,用户只需要向os提供盘块号,接下来的工作就可以由os来完成,间接达到了操控硬件的目的。

多进程使用盘块号

  上面讲解了单进程通过盘块号来完成,但我们知道,现代操作系统都是多进程。有多进程的地方,就会有竞争条件,这里也不例外,我们要怎样避免竞争条件呢,答案是使用请求队列。
  通过把对磁盘的请求组织成请求结构(struct request),并把多个请求结构组织成请求队列,由磁盘驱动从请求队列中拿出请求进行操作。那么这个请求结构主要包含的就是起始扇区号,读写命令,读写扇区数,有了起始扇区号,我们就可以根据前面讲到的公式来得到柱面磁头扇区等变量,进而对硬件进行操控。可能有的读者会疑惑,前面不是根据盘块号来得到柱面磁头扇区吗,这里怎么成了扇区号。其实扇区号和盘块号是差不多的,因为有了盘块号就可以得到扇区号,反之有了扇区号也可以得到盘块号,linux0.12中由盘块号得到扇区号是由req->sector=hb->b_blocknr << 1来完成的,这里的sector即是起始扇区,b_blocknr即是盘块号。
  ok,那么我们现在完成了多进程对盘块的基本使用,但是别忘了,操作系统还要让用户更加高效的使用,怎样高效的使用呢,在linux0.12中答案就是电梯算法,限于篇幅,这里不再展开来讲,相信书上和网上都有很多优秀的资料可以查看。

从文件字符流到盘块号

  从上面讨论的结果,我们只需要给os提供盘块号,那么os就可以替我们完成接下来的工作:组织成请求队列,拿出请求,算出柱面磁头扇区。进而磁盘开始工作。但这样还不是我们最终操控磁盘的方式,我们最终是通过文件来最终操控磁盘的。
  我们知道,文件内其实就是一串字符流,那只要知道字符流对应的盘块,我们就可以对磁盘进行操作了,所以现在我们的任务是如何把文件内的字符流映射到某个具体的盘块上。
  如何从字符流映射到盘块呢,可以使用数组,也可以使用链表,但这两种方法都有各自的局限性,linux0.12中是使用索引结构,也就是inode节点,这里的i也就是index的意思。注意i节点分为内存中的i节点和磁盘上的索引节点。前者除了记录后者的全部内容外,也会记录其他的信息如最后访问时间,i节点自身修改时间等,但我们这里的inode专指后者。inode节点中记录了文件的起始盘块号,有了这个,再加上我们知道每个盘块的大小,linux0.12中是2个扇区1024字节,就可以算出某个字符范围对应的盘块号了。
  举个例子,如我们要对test.c中第2099-2200个字符进行删除,首先我们首先要得到test.c对应的i节点结构,怎样得到呢,放到下一节来讲,这里我们先假设已经得到了test.,c的inode结构,那么就可以根据inode中的结构来得到存放相应数据的盘块号。每个盘块1024个字节,那我们想要操作的2099-2200自然在第二个盘块上(从第0个盘块开始),假设第2个逻辑盘块所对应的物理盘块是78,那我们就可以把78和操作数据量201传递给os,接下来的工作就是我们上面讲过的,组织成请求队列,拿出请求,算出柱面磁头扇区。最终磁盘开始工作。
  到现在,我们只需要拿到文件所在的inode,就可以传递给os让磁盘进行工作了,那怎样拿到文件所对应的inode呢,就引出了下面的目录和文件系统。

从目录到文件

  由上面的讲述我们知道,只需要拿到文件对应的inode,就可以让os操作磁盘进行工作了。那怎样拿到文件对应的inode呢?
  想想我们平时操作的文件,都是在某个目录下,看起来像是目录下面存放了多个文件。而实际上,目录也是一个文件,目录只是存放了目录下的文件名和对应的inode号,这个结构被称为目录项(dir_entry),这么一说是不是清晰了很多,只需要得到上级目录的inode,根据inode拿到上级目录的目录项,就可以得到文件的inode并得到盘块号,那上级目录的inode如何得到呢,答案是上上级目录的inode,这样最终会对应到根目录的inode。也就是说,只需要知道根目录的inode,就可以得到任何一个文件的inode,也就可以对任何一个文件进行操作。那么我们知不知道根目录的inode呢,答案是知道的,操作系统会自动为跟目录分配一个盘块并记录下来。那现在的故事就完整了,根据根目录的目录项,一级一级向下查找,直到查找到指定文件并得到指定文件的inode,就可以对os发出指令让磁盘开始工作。

struct dir_entry{
	unsigned short inode; //i节点号
	char name[NAME_LEN];	//文件名,长度NAME_LEN=14
};

从open系统调用到柱面磁头扇区

  最后,我们来看一下从open到磁盘工作的大致原理过程。首先open传入所操作文件的所在路径如/a/b/c.c,根据路径,我们首先查找根目录的内容,发现其中a对应的inode是2,接下来去找inode2对应的盘块号,也设为2,那么接下来去读2这个盘块号中存放的内容,得到"b":3,“c”:4,“d”,5,根据匹配,我们拿到b目录对应的inode号,也就是3,根据inode拿到b目录的数据盘块号,也设为3,再去读第3个盘块的内容,设为"c.c":“6”,“d.d”,7,根据匹配,我们拿到c.c对应的Inode号6,那么根据inode6拿到对应的盘块号,也设为6,接下来就可以把盘块号转换为请求结构,放入请求队列,从请求队列中拿出请求,转换为柱面磁头扇区,那么此时磁臂开始移动到指定的磁道,磁盘旋转到指定的扇区,磁头开始工作磁生电/电生磁,最后完成操作!

总结

  本文只是大致讲了操作系统完成磁盘读写的大概原理,一些细节并没有讲到,如磁盘高速缓冲区,高速缓冲是非常有用的,每次读写都要先经过高速缓冲,如果缓冲没有命中才会去进行磁盘操作。原理大概就是这样,实现起来却也比较麻烦,实现到最后,其实也就成为了一个文件系统的结构,如超级块,i节点位图,盘块位图,i节点数组,数据块等等。

下篇代码讲解已更新:【linux0.12】从open系统调用到磁头柱面扇区下篇-----代码讲解

参考资料

哈工大李治军老师视频
《Linux内核完全剖析----基于0.12内核》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值