[分享] 编程实现虚拟RAID读磁盘并发

[复制链接]

该用户从未签到

发表于 2015-2-3 12:44:30 | 显示全部楼层 |阅读模式
从事企业级数据恢复不可避免的需要对大量磁盘在软件层实现虚拟RAID,并对此进行操作,目前可实现虚拟RAID的常用数据恢复软件如:winhex、R-Studio、UFS explorer 等虽可以实现虚拟RAID,但实际上虚拟RAID并没有并发线程,也就是说,虚拟RAID最快速度也只是单块磁盘的速度,如果虚拟RAID中磁盘数量较多,做某些操作时,时间成本将大大提高。

自己进行的某些项目,也实现了虚拟RAID功能,但和WinHex、R-Studio、UFSexplorer等软件一样,操作磁盘也没有使用并发,原因一方面是代码不好设计,调试麻烦,另一方面也不想在此浪费时间,能实现功能就行,对于规模不大的虚拟RAID,时间也可以接受。

前些时间因为一个单子,最后还是决定实现虚拟RAID的并发功能。

那个单子是一个28块1TB磁盘做的RAID5,因为文件系统特殊,需要在软件层重组虚拟RAID,并对其进行镜像,磁盘数量较多,winhex 不支持,其它软件也不支持镜像到磁盘,最后只得在自己的软件上添加克隆功能进行镜像。由于软件没有虚拟RAID的并发功能,镜像速度也只相当于一块磁盘的速度,而源盘型号较垃圾,速度慢,镜像速度只有一分钟6-7GB,此RAID需要镜像的空间超过20TB,镜像时间超过2天。客户还是在外地,上门工作,使得经济成本大大提高。

  目前在做的项目已实现了虚拟RAID的并发功能,实测用3块磁盘(每块磁盘最高速度约11 -12GB每分钟) 的做的虚拟RAID0,读速度达到34GB每分钟; 同样的介质做成虚拟RAID5后,读速度达到23GB每分钟。当然,如果是做镜像或是解析文件系统进行数据导出的话,还要看目标介质的写入速度了。

001.jpg

002.jpg

问题:影响虚拟RAID 速度的因素

1:RAID 块大小。

2:读磁盘速度。


RAID 块大小因素:

解决思路:

如果RAID块大小为1扇区,如果一次请求1024个扇区。一般思路:一个块一个块的读,此次请求需要执行1024次I/O 操作,这将对速度有致命影响,WinHex、R-Studio、UFS explorer等软件对于此种情况,速度下降10倍以上。

解决方案:通过请求的开始扇区号和要读取的扇区数,计算出每块磁盘在此次请求中要读取的开始扇区和扇区数,这样对每块磁盘只进行一次I/O 操作,然后在内存中重新按RAID格式组织缓冲,返回。



如果RAID块大小为2048扇区,如果一次请求1024个扇区,一般情况下这次请求的扇区都在一个块内,也就是说只需从一块磁盘中读取,并发无作用,速度是单块磁盘的速度。这种情况只有通过提高要读取的扇区数来解决。



读磁盘速度因素:

解决思路:

如果一次请求1024扇区,RAID块大小为128扇区,此次请求最少需要操作8个块,如果这个8 个块分布在8 个磁盘上,则需要读取8个磁盘。WinHex、R-Studio、UFSexplorer软件的思路:读1号磁盘->读1号磁盘完成->读2号磁盘->读2号磁盘完成->读3号磁盘…


此方法较好控制,软件不容易出错。如果使用并发,此次请求的时间将为原来时间的1/8(理论上),就是同时创建8个线程,每个线程读一块磁盘,待8个线程执行完成,再进行缓冲整理,返回。当然也可以8个线程进行缓冲整理,但缓冲整理所需时间很少,不是影响速度的因素。


下面是伪代码:

自定义函数 isREAD_DiskC  // 判断所需磁盘是否读完,此函数要使用线程互斥,同时只
// 能一个线程使用,否则读完磁盘计数的变量数据容易被覆盖


Begin

   读完磁盘计数的全局变量+1;

如果 (读完磁盘计数 = 这次请求要读的磁盘数) 就

   继续主控制线程:Main_control;

   关闭当前线程;

End;

  

自定义函数 RAID_READ_DISK(read_parameters)  // 每个线程读磁盘的函数

Begin

   根据结构体指针 read_parameters 中的数值读取磁盘扇区数据; // read_parameters 记录要读的磁盘介质,
//开始扇区号,要读的扇区数,所读到的缓冲地址

   读完后关闭此单个磁盘的读线程;

   创建一条线程执行 isREAD_DiskC 函数;

End;

自定义函数 Main_control        ////// 主控制线程

Begin

  通过请求的开始扇区号和扇区数 用 自定义函数:GetNeedSectorInfo 得到要读的磁盘列表和取出每块磁盘要读的开始扇区和扇区数;

  循环要读的磁盘数量

   Begin

       read_parameters := 此块磁盘 GetNeedSectorInfo函数中得到的值;

       创建一条线程 执行RAID_READ_DISK(read_parameters);

End;

执行Main_control 函数的线程挂起,等待自定义函数isREAD_DiskC 的指令继续;

  要读的磁盘全读完后,缓冲整理过程 ......;

End;


实际上使用并发,代码量不会多多少,只是线程难以准确控制,也不好调试。

这其中要注意缓冲数据覆盖和全局控制变量的一致性,特别是在虚拟RAID中有成员为另一个虚拟RAID的时候。
  • TA的每日心情
    开心
    2014-12-25 02:29
  • 签到天数: 45 天

    [LV.5]六品通判

    发表于 2015-2-3 18:18:58 | 显示全部楼层
    哇,好东西啊!!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2014-12-25 02:29
  • 签到天数: 45 天

    [LV.5]六品通判

    发表于 2015-2-3 18:19:26 | 显示全部楼层
    一直纠结于这个速度,借此试试!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-11-4 18:52
  • 签到天数: 277 天

    [LV.8]三品御史

    发表于 2015-2-6 05:06:37 | 显示全部楼层
    厉害,膜拜了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-8-19 23:26
  • 签到天数: 25 天

    [LV.4]七品知县

    发表于 2015-2-23 10:17:16 来自手机 | 显示全部楼层
    学習了~非常感謝!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2015-5-25 20:59:28 | 显示全部楼层
    如果仅仅是复制一些特定后缀的文件 两个USB口分别对应组成RAID0的两块硬盘 并行写入的时候如何确定写入的位置呢? 是不是自己要添加MFT表中的记录? 问题来了 添加记录的话 如何确定添加记录的位置呢?
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-12-28 19:52:47 | 显示全部楼层
    很好 我很喜欢
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-12-28 21:59:44 | 显示全部楼层
    很好 我很喜欢
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2018-4-15 08:04
  • 签到天数: 357 天

    [LV.8]三品御史

    发表于 2017-1-11 21:28:15 | 显示全部楼层
    真厉害,太深了
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    快速回复 返回顶部 返回列表