查看原文
其他

硬盘就是一个旋转餐桌!

SunnyZhang 码农翻身 2021-04-20

我是一个硬盘,在我们这个大家族中,我属于通过机械装置定位并读取数据的机械式硬盘,从速度上说,和我的兄弟SSD硬盘没法比,但是我的容量更大,更便宜啊!


来,爆个照:


磁盘内部图-我的肖像照


无论是读数据还是写数据,都要通过我的胳膊才行。我的胳膊长成下面这个样子,非常雄壮,但却有点笨重,移动起来稍稍有点缓慢。当操作系统大哥要我读写数据的时候,我就挥动胳膊把数据从盘片上取出来。想想这个动作就知道我读取的速度有多慢了。


磁臂实物图


如果你还是不清楚我为什么慢,那么就想想吃饭的场景吧。


假设你坐在一个超大的旋转餐桌面前吃饭,餐桌上放了两圈盘子,都是美味佳肴,你瞄上了你最爱的东坡肉,就在对面位置,就在内圈。你伸出胳膊,举起筷子,“悬停”在内圈盘子上方,然后用左手慢慢转动餐桌,等东坡肉到你面前的时候,你以迅雷不及掩耳之势把最肥的一块肉夹起来放到你的盘子里面。这其实就是我读取数据的过程。


旋转餐桌


我的实际结构比餐桌复杂多了,我的数据是存储在一个有磁性的圆盘(盘片)上面的。而这个圆盘又被划分为一个一个的同心圆(这个同心圆成为磁道),我的数据就是存储在这些同心圆(磁道)里面的。


磁盘的盘片及磁道


你可能明白为什么机械硬盘这么慢了,因为我们读取(或者写入)一个数据的过程太复杂了,因此消耗的时间也就长很多,具体时间包括寻道时间(伸胳膊时间)、旋转延迟(转餐桌时间)和传输时间(夹菜的时间)


为了能让我肚子里面装更多的东西,我的发明者把多个上面的磁盘罗列在一起,构成了下面的结构。就好像把平房改造成楼房一样。虽然使用的土地的面积没变,但可以住的人(存储的数据)却多多了。


磁盘立体结构



蓄流/泄流与请求合并重排



由于我很慢,操作系统大哥想读取数据的时候就不得不照顾一下我的感受了。


当上层应用发送请求的时候,操作系统大哥不会马上把请求发送给我让我处理,而是稍微攒一会儿,等到一定数量后一起发给我。


为什么要攒一会儿呢?这是因为我的胳膊摆来摆去实在是太慢了,要是一会儿让我从最里面拿东西,一会儿又让我从最外面拿东西,简直是要我的老命。


这种方式叫做蓄流,接下来操作系统大哥就可以做更多事情了。其中主要包括请求合并和请求重排序。


所谓请求合并就是如果新的请求与已有的请求可以首尾相连的话,它会把这两个请求合并成为一个请求。


请求重排序则是将请求按照逻辑地址(简称LBA)排序,保证请求是有序的。当上层应用发送按时间顺序的1、2、3、4和5等五个请求的时候。此时,操作系统大哥并不会按照时间顺序发送给我,而是按照图中红色虚线箭头的顺序(1、5、2、4、3)发送给我。这样,我就不用来回挥动我笨重的胳膊(磁臂)了。


 IO重排序



注:对于磁盘来说,磁盘寻道的时间往往是最长的,因此通常是先寻道,然后在等待盘片旋转到期望的位置,这样就可以读数据了。比如对于15000转/分钟的磁盘,寻道时间大概在2-8毫秒之间。而旋转延时平均在2毫米左右。因此即使是相邻的两个磁道,来回摆动一下可能就超过4ms了,因此,磁盘总是尽量将一个磁道上数据读完之后再读其它磁道。


作为吃货,还回到吃饭这件事上来,哈哈。


这就好比我先后想吃吃宫保鸡丁、京酱肉丝和地三鲜。如果没有蓄流/泄流机制,那我就要转桌子,夹宫保鸡丁,然后吃起来。吃完后,找京酱肉丝在哪,然后又转桌子,夹京酱肉丝,欢快的吃。不断重复这个过程。但有了蓄流/泄流机制后,就好像我提前规划好了要吃那些菜,然后用一个大铲子定位在菜转过来后的位置,每当一个菜到我这时我就用铲子铲一点,等我的胳膊回来的时候铲子里面就有各种我想吃的菜了。


最后期限


虽然操作系统大哥充分照顾了我的情绪,但上次的应用程序可能会有意见了。比如有些关键的应用,如果请求长时间没有响应,它会认为这个请求失败了,从而进行错误处理。然而,实际上请求并没有失败,而是在等着我处理其它请求呢。


我们举个例子,依然是按时间顺序的,1、2、3、4、5、6、7、8和9。如果按照前面逻辑地址的顺序,那么处理请求的先后顺序是图8的样子(1、3、4、5、6、7、8、9和2)。


本来2很早就发过来了,但是由于它太远了,最后才被处理。如果上层应用程序要得很急,那就不可接受了。


排序访问顺序


为了解决上面的问题,操作系统大哥想出了一个好办法。这个办法就是每个请求都加一个最后期限时间(Deadline),优先处理快到最后期限的请求。


Deadline策略


其思想就是在请求加入队列前记录该请求的最后期限,而分发的时候以此时间为标准进行分发。依然以上面10个请求为例,由于请求2比较远,因此会被延时处理。


在处理的过程中可能后来的请求可能由于合并和重排序等原因导致请求2延后执行,但由于有最后期限,其被处理的时间不至于太晚。


“公平”调度


大家知道,操作系统大哥“同时”会运行很多应用程序,大家为了完成人类的需求,工作时都会拼命将磁盘请求发给操作系统。


这一天出现了一个怪现象:有两个非常勤奋的应用程序,小A和小B,他们俩不断地通过操作系统大哥向我发出读写请求。  可是小A的请求很快就能得到回复,而小B的请求应答总是姗姗来迟有时候等到花儿已经谢了还不见踪影。 


小B急了,去质问操作系统大哥:“这到底是怎么回事?怎么这么不公平!”


操作系统大哥说:“别急别急,我跟磁盘兄弟沟通一下,看看啥情况。”


经过周密的调查以后发现,原来小A所需数据总在5号磁道附近,而且小A访问的又很频繁;而小B的数据则在非常远1000号磁道附近。操作系统大哥将请求排序后发给我后,大多数请求都是5号磁道附近的。


对于我来说,并不知道这些数据是属于那个进程,我只是奉命处理而已。这样就会导致一个问题,就是小B的请求会迟迟得不到处理。


这种应用程序总是访问某个区域数据的特征叫做数据的区域局部性(也就是某个应用的数据通常是集中在某个区域) ,为了解决这个问题,操作系统大哥实现了另外一种调度策略(CFQ),这种调度策略兼顾每个进程的公平性。


简单来说:操作系统大哥为使用磁盘的所有进程分配一个请求队列和一个时间片,在调度器分配给进程的时间片内,进程可以将其读写请求发送给磁盘,当进程的时间片消耗完,进程的请求队列将被挂起,等待调度。 


每个进程的时间片和每个进程的队列长度取决于进程的IO优先级,IO优先级从高到低可以分为三大类:RT(real time),BE(best try),IDLE(idle), 操作系统将会将其作为考虑的因素之一,来确定该进程的请求队列何时可以获取磁盘的使用权。


也就是说,操作系统大哥在向我发送请求的时候会考虑每个进程的感受,保证每个进程被处理的请求的量是近似一致的。


介绍到这里,我今天已经把我自己和操作系统大哥为我所做的东东都介绍给大家了。如果有什么不清楚的地方,还请大家告诉我,我再一一道来。


码农翻身公众号开放投稿,可能是全网最高片酬:

用故事讲技术 ,稿费1000

技术/职场/感悟/面试等,稿费700

翻译类文章,每千字200

联系方式:onlyliuxin97(微信)

详情猛戳: 可能是全网最高片酬,速来!


往期精彩回顾

我是一个线程

我是一个Java Class

面向对象圣经

函数式编程圣经

TCP/IP之大明邮差

CPU阿甘

我是一个网卡

我是一个路由器

一个故事讲完HTTPs

编程语言的巅峰

Java:一个帝国的诞生

JavaScript:一个屌丝的逆袭

负载均衡的原理

阅读源码的三种境界

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存