小明飞快的从家中赶往火车站,准备买今天的火车票,早点出发到二叔家。因为,不是春运时段,所以,买当天的火车票还是没有问题的。没多久,小明就感到了深圳火车站,买到了前往二叔家的车票,飞快的进入候车室候车。小明看着车站来来往往的人,思绪又不知不觉的飞快的旋转进入linux世界。
为什么要有火车呢?最早期是由于西方的工业革命发展,蒸汽机的发展取代了马车,还可以批量运送大量的人。到了现代,虽有小汽车,但火车还是存在着,主要还是因为火车可以批量运送大量的人,同时,也有并不是所有的人都买的起车,比较经济实惠。另外,从国家的战略来讲的话,国家还需要有几条交通大动脉,准备一旦有战事起的时候,可以快速的运送兵力,说到这,又让小明想起了抗战的时候,中国军队早期战败的一个主要原因是交通线不发达,兵力的机动能力太差,结果像淞沪这样的大会战,变成了“添油战”。不管怎么样一个原因,其中一个重要的特性,就是火车具体“批量运送的能力“。这是我主要想说的,其实,对应到linux内核的设备驱动中,块设备具有同样的属性。因为,在计算机体现结构中,除了网络存储外,磁盘算是存储层次结构中最末一层的存储层次了,价格便宜,但速度慢,主要由于其物理特性决定的(磁臂上的磁头来回移动寻道的速度慢),所以,涉及到磁盘上的数据的操作都尽量“批量的处理”。也就是最好一回读取数据能够读取一遍,而用户最好也访问相邻的数据,这样下来最高。当然,块设备的属性还不只这些,像块设备可以随机访问,通常字符设备是流式访问,也就是顺序访问。在前文我们说过,可以把一个小汽车比喻成一个字符设备,主要就是数据量少,而我们今天主要强调的就是批量处理的块设备。
接下来让我们看看块设备驱动程序在内核中所涉及的层次图:
注:图右侧(即蓝色粗线后边)的内容是内核中涉及到的相关数据结构,为了便于大家理解,特以图形的形式整理了一下。从右侧的数据结构来看,每一层有相关的数据结构的同时,各个层面之间的数据结构是相互关联的,大致的关联关系已在图中以箭头的方式表现出来。
从上面的图中我们发现,块设备驱动比字符设备驱动复杂的多,在虚拟文件系统和驱动程序之间有很多附加层,主要有:映射层(其实在映射层上面还有一个磁盘高速缓存,这个不是本文描述的重点,省略)、通用块层、I/O调度程序层。之所以加上这些附加层,就是为了充分利用磁盘的物理特性,尽可能的减少物理操作或者每一次物理操作尽可能的访问批量内容。他们的功能大抵如下:
映射层:在这一层,内核确定文件所在块大小,并根据文件块的大小计算所请求数据的长度,接下来,映射层调用文件系统具体函数,最后总确定在所请求数据在磁盘上的位置。
通用块层:启动I/O操作来传送所请求的数据。这一层主要是为了隐藏硬件设备间的差异。为所有块设备提供了一个抽象视图。
I/O调度程序层:根据预先定义的内核策略将待处理的I/O数据传送请求进行归类。将请求最终传到块设备驱动程序,块设备驱动程序最终向磁盘控制器的硬件接口发送适当的命令,进行数据传送。
每个组件及层次管理的磁盘数据长度不同,硬件块设备控制器采用“扇区”、虚拟文件系统、映射层和文件系统采用“块”,而块设备驱动程序采用“段”,磁盘高速缓存采用页管理(本文中不涉及)。
那么当我们从文件中读一段内容时,具体过程是怎样的呢?
read系统调用--》VFS函数确定所请求的数据是否已经存在(优先考虑高速缓存)--》内核映射层根据文件的块的大小及具体文件系统函数来确定数据在磁盘上的具体位置 --》通用块层启动I/O操作来传送所请求的数据 --》I/O调度层根据预先定义的调度策略对请求归类,并向设备驱动层传递请求 --》设备驱动根据请求实际操作设备控制器进行数据操作。
那么怎么样和我们的现实生活联系起来呢?既然是大话,就尽量找我们可以理解的通俗易懂的方式来理解。我始终坚持的观点就是:只要是人写的程序,就脱离不了人得思维,在现实生活中总会找到原型。好了,开始我们的大话过程:在前文说过,虚拟文件系统可以想象成政府提供给百姓的办事接口(也就是kernel提供给user的接口),百姓可以通过这个接口向政府提供服务,而所有的具体服务的过程都隐藏在接口的内部,对百姓不可见。尽管这样,我们有过坐火车经历的同学还是可以了解个大概。
第一,我们需要到售票口买票,买票的时候要提供几个信息:车的类型,是普快还是高铁?是硬座还是软座?有没有特殊要求,希望做第几个车厢,第几排等。--》对应read系统调用过程,其信息相当于read时候提供的参数
第二,在火车站经常会遇到有人有临时事情需要转票的或者倒票的(当然,这不鼓励),你若是能够遇到这样的,就不用排队了,直接转了票就可以候车,效率高。--》是否所请求的数据已经在高速缓存中
第三,售票员要确定你做的车厢是否还有位置,你做的普快还是高铁,不同的车型车厢所容纳的人数不同,给你安排在那个车厢第几排等--》内核映射层所干的工作
第四,当上一步售票员确定了这些信息后,就会发出一个出票的请求。--》通用块层启动一个请求
第五,无论是系统还是售票员往往比较喜欢团购,这样,一次就可以批量处理,效率高。若是一个队伍不细分的话,售票员往往比较辛苦。我们经常在一些二线城市或者乡镇的小车站,还会遇到一进车站,就会看到有车站的服务人员拿个大喇叭在那指导乘客,去不同的地方应该在那个队伍排队买票,不要插队,在车站一般采用的策略就是FIFO(先进先出)策略,来的早的排在前面。但这个策略并不是唯一的,例如有老人或者儿童可能就可以走特殊渠道,或者插队或者在专用通道。在上车的时候,车站服务人员还会提醒“到XX地方的乘客注意了,你所乘的列车已经到站,请准备好检票”,这个时候,去往同一个地点乘同一辆车的人都会排成一队,无形中就是对相同的请求进行归并。否则,挨个问火车站服务人员多累呀,效率多低呀。--》I/O调度层往往对请求进行归并,根据调度策略尽量批量处理。linux下的调度策略是可以定制的,而现实中的策略往往是既定的。
第六,售票系统根据请求,出票后,售票人就可以上车了。而车站往往有专门的指示牌和通道,及工作人员来指导在同一车次的乘客应当往哪个方向走,你应当上哪个车厢,对应的车厢验票员要验票,最后,你坐到对应车厢的对应座位上。--》设备驱动程序操作磁盘控制器操作具体位置上的数据。整个车站指示牌及工作人员的指示等过程相当于设备驱动程序做的工作,而我们乘客就相当于具体的数据。每一节车厢就相当于设备驱动程序所操作的“块”,是一组数据。至于“扇区”我们可以理解成在这里也是一个车厢,那么就相当于块的大小等于扇区的大小。
从上面的这个过程,理解块设备驱动程序的整体就非常形象了。想到这里,就听到车站喇叭响起“小明旅客,你要乘坐的火车即将启动了,请您尽快等车。。。。。。”,此时,小明拎起行李飞快的冲向火车站口。。。。。。
阅读(981) | 评论(0) | 转发(0) |