SSDsim源码分析之find_nearest_event()

SSDsim源码分析之find_nearest_event()

在介绍find_nearest_event()函数之前,我们很有必要了解下关于SSDsim的一些必要的模拟驱动原理:

SSDsim模拟驱动原理

SSDsim可以提供对时间和能耗的模拟。而首先我们先介绍下时间模拟部分的基本原理和依据;

通常大多数模拟器的驱动方式分为三种类型:即时间驱动,事件驱动和请求驱动。

采用时间驱动方式的模拟器需要先设置一个固定的时间步进长度,每当系统前进一个步进时间窗口,系统时间增加一个响应的步进长度,在针对处理器的模拟器中经常用到这种类型的模拟方法;

而基于事件驱动的模拟器则是根据系统内部各个部件的状态变化来修改系统时间,SSDsim就是采用的这种方式的驱动方法;

请求驱动是根据外部请求的到达来修改系统时间,这种方法实现简单,但是精确性不高,所以一般不考虑。

在SSDsim中,内存颗粒(chip)是固态硬盘中的独立服务单元,闪存通道(channel)相互之间也是独立的,多个内存颗粒chip分布在同一个闪存通道channel上,在SSDsim中,将这些单元统称为独立单元。
所以我们可以清晰而且确切的知道,在SSD中,每一个channel都是可以处理自己的IO队列请求的,所有的channel并行处理属于自己的IO队列请求。
其中每个独立单元都设置四个时间和状态变量:当前状态、当前状态时间、下一状态、下一状态预计时间。当前状态CURRENT_STATE表示这个独立单元的当前所处的状态,下一状态NEXT_STATE表示这个独立单元从当前状态将进化到的下一状态。
宏观上来看:固态盘中的闪存颗粒和闪存通道都存在两种状态,既工作状态(busy)和空闲状态(idle)。
从微观上看:根据读、写、擦除的状态变化,闪存颗粒chip存在主要的四种状态,包括空闲状态、接收命令地址状态、介质操作状态(又可分为读写)和数据传输状态,后三种状态使得闪存颗粒处于工作状态;
闪存通道channel存在三种状态,包括空闲状态、数据传输状态和命令地址传输状态,后两种状态使得闪存通道处于工作状态。
下面的表格介绍了SSDsim中两种独立单元的所有状态表示和相应的时间损耗参数:

这里写图片描述

这里写图片描述

从上面可以看到channel通道的状态变化流程图和chip芯片的状态变化流程图,我们可以根据这个状态变化图理解channel和chip的状态变化规律。
例如,对于一个执行读操作的内存颗粒chip而言,假设当前状态是接受命令地址状态(CHIP_C_A_TRANSFER),则下一个状态必然是读介质状态(CHIP_READ_BUSY),如图中所示从状态7到状态6的转变。
而对于channel来说在执行一个写操作的请求时如果当前channel状态是命令地址传输状态CHANNEL_C_A_TRANSFER则下一状态必然是数据传输状态CHANNEL_DATA_TRANSFER,如图状态2到状态3的转变。

状态参数表给出了各个状态的停留时间。独立单元的下一状态预计时间就是依据每个状态的停留时间计算获得。在SSDsim中,状态的转变就是一个事件,每发生一次事件都将导致SSDsim的系统时间向前推进。
具体来说,时间的推进方法是:已知每个独立单元的当前状态,和当前状态时间,同时下一状态和下一状态的预计时间可以通过状态转换图和状态参数表获得,在所有独立单元中寻找最先到达的下一个状态的时间,作为SSDsim的新的系统时间,周而复始地使模拟不断向前推进。通过对所模拟的固态盘内的每个状态进行观测,可以准确地得到对每条外部请求的响应时间,从而完成SSDsim的时间模拟。

通过对模拟原理的介绍,我们现在可以开始详解find_nearest_event()函数的功能了,它的主要任务就是寻找所有独立单元的channel下和每个channel下所有独立单元chip的状态情况,从中确定出当前ssd所有子请求中最早到达的下一状态时间。
为了便于理解,我们先设定一个专用名词:事件——也就是独立单元channel和chip一旦有存在状态转变(也就是当前状态,当前状态时间,下一状态,下一状态预计时间都存在且合理时),那么就代表着有事件发生,SSDsim要驱动模拟时间参数的变化。接下来我们先看一看整个函数的流程:

首先函数会先定义三个初始参考变量值time、time1和time2,且全初始化为0x7fffffffffffffff)(无符号_int64类型的最大值)。随后进入一个for循环针对每一个channel进行一个所有事件的查询过程:
在for循环中,若当前也就是第i个channel的下一状态为非空闲状态,则说明此时channel已经在空闲状态,那么就说明下一状态一定是工作状态,但是有两种可能的状态情况:

此时IO为读请求,但是有可能命令地址状态传输尚未开始或者数据传输尚未开始;这两个阶段都是可能的情况之一;

此时IO为写请求,那么只可能下一状态是命令地址传输状态。
而无论是上述任何一种状态,程序流都会直接进入内部for循环判断该channel下的所有chip中的事件情况。这是因为此时需要从chip中判断出其他事件,当chip中的事件完成后,才能成功触发channel的事件。

否则说明channel为空闲,那么下一状态就有可能产生事件。因此程序直接判断channel的下一状态预计时间是否小于0x7fffffffffffffff,如果大于则说明该预计时间不合理,此时直接忽略channel状态转变的可能性,转向该channel下所有chip的逐一状态转变判断,查看是否有其他事件。
而如果小于0x7fffffffffffffff成立,那么就证明channel的下一状态预计时间是可以达到的,此时需要再继续判断下一状态预计时间是否比当前ssd的系统时间大:
如果下一状态预计时间大于当前系统时间,则说明该IO是非阻塞的,时间顺序性是正常的,那么就得用time1记录这个下一状态预计时间。(并且要注意,自此往后的继续查询下一个channel的事件时,就开始用这个time1来比较查询其他channel的所有下一状态预计时间,并且从中获取最小值;time2也是一样的原理)
而如果下一状态预计时间小于当前系统时间,说明该IO请求被阻塞,此时就得查看下channel下chip的事件情况。

至此,函数会进入内部for循环查看当前通道i的chip情况:
首先,函数会判断当前的芯片j的下一状态是否为空闲状态或者数据传输状态;
若都不是,那么说明此时芯片j没有事件发生。因为如果下一状态既不是空闲状态也不是数据传输状态,那么只能说明此时chip的状态是处于空闲状态。但是channel通道i已经有事件条件的触发,所以此时证明对应的事件并不是发生在当前的chip芯片j中,继续判断下一个chip;
如果是空闲状态,那么chip当前状态会有两种情况:分别是读操作时的数据传输状态和写操作时的介质写状态。此时需要判断下一状态的预计到达时间的合理性,如果不合理的话直接转向下一个chip的判断;如果合理的话则继续判断该IO是否阻塞。若IO非阻塞则用time2记录当前的下一状态预计时间作为最小参考值之一;否则说明IO阻塞,需要继续查看下一个chip。
当遍历查询完当前channel_i下所有chip的所有状态情况后,我们就可以得到chip中此时最小的time2,此时这个time2代表了通道i下面所有chip中最近的一个事件(因为通过了内部for循环每一次time2的更新后,如果是最近的事件,那么说明IO非阻塞并且下一状态预计时间最小)。
随后,程序流会继续在外部channel的for循环中继续遍历所有通道channel下的事件情况,并且最后成功取得channel和chip中所有合理事件中最近的一个预计到达时间,分别记录在了time1和time2中;
接着便直接用time记录time1和time2中的最小值,并且返回time,至此,整个find_nearest_event()函数便完成了一次最近事件预计时间的获取。
但是,如果在整个过程中,程序并未成功在channel和chip中找到合理并且存在的IO和对应的下一状态时间,那么此时返回的就有可能是0x7fffffffffffffff这个时间极限值(time1和time2都是0x7fffffffffffffff)。所以,如果返回的是0x7fffffffffffffff,说明最近的下一状态时间不可获取,证明此时IO全部被阻塞或者全部事件都处理完成了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值