disksim的代码真的是不好读啊。
首先从disksim_main.c开始里面是disksim开始执行的地方。从disksim开始运行到发请求在RAID上的重新映射的函数流程是这样:
disksim_main.c::disksim_run_simulation() -----> disksim.c:disksim_simulate_event(0) ------>curr=getnextevent
------>disksim_iosim.c:io_internal_event() ----------->case IO_REQUEST_ARRIVE ------------>disksim_iodriver.c:iodrver_request()
------>disksim_logorg.c:logorg_maprequest()
这里先重点看logorg_maprequest():
req = (outstand *) getfromextraq(); ASSERT (req != NULL);
而getfromextraq是得到一个经过初始化的空的req变量
req->arrtime = simtime; req->bcount = curr->bcount; req->blkno = curr->blkno; req->devno = curr->devno; req->flags = curr->flags; req->busno = curr->busno; req->buf = curr->buf; req->reqopid = curr->opid; req->depend = NULL;代码中的bcount是指请求大小(以块为单位),其他的就是把event变为请求,关于event和请求之间的关系,可以看disksim源代码doc目录下的outline文件
然后再看紧接着for循环内部的一个else:
else { /* ???? */ /* for (j = 0; j < logorgs[i]->numdisks; j++) { */ for(j = 0; j < logorgs[i]->actualnumdisks; j++) { if (curr->devno == logorgs[i]->devs[j].devno) { if(logorg_overlap(logorgs[i], j, curr, logorgs[i]->blksperpart)) { logorgno = i; break; } } } if (logorgno != -1) { break; } }RAID有两种Addressing mode(详见Manual 3.8节),自己暂时只能理解Parts,代码中的logorg就代表磁盘阵列的组织方式,其中请求从trace下发的时候是针对某个设备下发的,也就是有设备号。就是对于trace来说,它可能想着往某一块硬盘里写数据,但是经过disksim在RAID下对访问块号和设备号的重新映射,最后影响的磁盘可能不止一个。而这里先检查在对应的logorgs[i]内部的磁盘中是否有trace请求对应的设备,如果有的话,这次请求就在该logorgs[i]上处理就可以了。且logorg_overlap()函数会检查对应的磁盘是否能满足请求。检查完毕后,接下来就会在该logorgs[i]上进行处理:
maptype = logorgs[logorgno]->maptype;
reduntype = logorgs[logorgno]->reduntype;这里有两种情况,如果在参数中设定了Parity_*系列的参数,那么maptype会变为ASIS,(可以参见logorg_initialize )。如果没有设定Parity_*那么maptype就是参数中设定的maptype。如果理解了Parity_rotate参数的设定,其他maptype映射类型就很好理解了。所以来看一下logorg_parity_table()函数(如果参数为Parity_rotate,那么在logorg_initialize中会被转换为PARITY_TABLE)。为了重新映射请求,disksim会创建一个表格来存储对应的映射。下面是一个18个磁盘组成的RAID5对应的表格,每一个元素第一项对应设备号,第二项对应块号。因为可能是多个块组成一个stripe unit,多个stripe unit组成一个stripe.
有了这些知识后再来看logorg_parity_table():
if (currlogorg->addrbyparts) {
curr->blkno += curr->devno * currlogorg->blksperpart; }这里需要把块号重新映射为一个总的块号,因为trace想的块号是在一个磁盘用完了再用第二个磁盘,而使用raid后是把数据分散在每个盘上,所以需要重新映射,这里先计算出一个总的块号来进行重新映射。
unitno = curr->blkno / stripeunit; stripeno = unitno / partsperstripe; tablestart = (stripeno / currlogorg->tablestripes) * currlogorg->tablesize;//该条带号对应的表的开始块号 stripeno = stripeno % currlogorg->tablestripes;//该条带号对应的表中的索引 blkno = tablestart + table[(stripeno*(partsperstripe+1))].blkno;//该条带号对应的开始块号(对当前硬盘)一个stripe unit是有stripeunit个块组成的,所以unitno代表的是对应的第几个strip unit。然后stripeno对应的是第几个stripe。而tablestart和blkno都是对应于单个磁盘而不是整体的块号。