Raid 磁盘阵列管理
磁盘阵列(Redundant Array of Independent Disks,RAID),有“独立磁盘构成的具有冗余能力的阵列”之意。磁盘阵列是由很多价格便宜的磁盘,组成一个容量巨大的磁盘组,利用个别磁盘提供数据所产生加成效果提升整个磁盘系统效能。SylixOS 实现了软件 RAID 功能,配置灵活、管理方便,可以实现将几个物理磁盘合并成一个更大的虚拟设备,从而达到性能改进和数据冗余的目的。
SylixOS 中关于软件 RAID 磁盘阵列管理的内容主要在“libsylixos/SylixOS/fs/diskRaid”目录下,实现了 RAID0 与 RAID1 级别。
RAID 0
RAID 0 是最早出现的 RAID 模式,即 Data Stripping 数据分条技术,把连续的数据分散到多个磁盘上存取。当系统有数据请求就可以被多个磁盘并行的执行,每个磁盘执行属于它自己的那部分数据请求。这种数据上的并行操作可以充分利用总线的带宽,显著提高磁盘整体存取性能。因为读取和写入是在设备上并行完成的,读取和写入性能将会提高,这通常是运行 RAID 0 的主要原因。但 RAID 0 没有数据冗余和容错,如果驱动器出现故障,那么将无法恢复任何数据。其存储结构如下图所示。
在 SylixOS 中创建 RAID 0 类型磁盘阵列块设备的函数接口如下:
#include <SylixOS.h>
ULONG API_DiskRiad0Create (PLW_BLK_DEV pblkd[],
UINT uiNDisks,
size_t stStripe,
PLW_BLK_DEV *ppblkDiskRaid);
函数 API_DiskRiad0Create 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误码。
- 参数 pblkd[] 是物理块设备列表。
- 参数 uiNDisks 是物理磁盘数量,只能为 2 块或者 4 块,二者选其一。
- 参数 stStripe 是磁盘条带数量,必须是扇区字节数的 2 的指数倍,最好为 32KB。
- 参数 ppblkDiskRaid 是返回的 RAID 虚拟磁盘。
API_DiskRiad0Create 函数将多个物理磁盘合并成一个更大的虚拟设备,阵列中所有的物理磁盘参数必须完全一致,例如磁盘大小、扇区大小等参数必须相同。函数中最终返回的 RAID 虚拟磁盘设备类型使用 LW_DISKRAID0_CB 结构描述,其具体结构如下:
typedef struct {
LW_BLK_DEV DISKR_blkdRaid; /* DISK CACHE 的控制块 */
PLW_BLK_DEV *DISKR_ppblkdDisk; /* 阵列物理磁盘列表 */
UINT DISKR_uiNDisks; /* 阵列磁盘数量 */
UINT DISKR_uiDiskShift; /* 磁盘 shift */
UINT DISKR_uiDiskMask; /* 磁盘掩码 */
UINT DISKR_uiStripeShift; /* 条带基于扇区的 2 指数次方 */
UINT DISKR_uiStripeMask; /* 条带掩码 */
} LW_DISKRAID0_CB;
typedef LW_DISKRAID0_CB *PLW_DISKRAID0_CB;
磁盘 shift 和磁盘掩码都与组成阵列的磁盘数量有关,RAID 阵列的 BLK IO 控制块为最终向上表现出的虚拟设备,其中的读接口函数的具体实现如下:
static INT __raid0DevRd (PLW_DISKRAID0_CB pdiskr,
VOID *pvBuffer,
ULONG ulStartSector,
ULONG ulSectorCount)
{
PUCHAR pucBuffer = (PUCHAR)pvBuffer;
ULONG ulNSecPerStripe = (ULONG)(1 << pdiskr->DISKR_uiStripeShift);
ULONG ulRdCount;
ULONG ulStripe;
ULONG ulSector;
UINT uiDisk;
while (ulSectorCount) {
ulStripe = ulStartSector >> pdiskr->DISKR_uiStripeShift;
uiDisk = (UINT)(ulStripe & pdiskr->DISKR_uiDiskMask);
if (ulStartSector & pdiskr->DISKR_uiStripeMask) {
ulRdCount = ulStartSector & pdiskr->DISKR_uiStripeMask;
if (ulRdCount >= ulSectorCount) {
ulRdCount = ulSectorCount;
}
} else {
if (ulSectorCount >= ulNSecPerStripe) {
ulRdCount = ulNSecPerStripe;
} else {
ulRdCount = ulSectorCount;
}
}
ulSector = ulStartSector >> pdiskr->DISKR_uiDiskShift;
if (RAID_BLK_READ(pdiskr, uiDisk, pucBuffer, ulSector, ulRdCount)) {
return (PX_ERROR);
}
ulStartSector += ulRdCount;
ulSectorCount -= ulRdCount;
pucBuffer += (ulRdCount * pdiskr->DISKR_blkdRaid.BLKD_ulBytesPerSector);
}
return (ERROR_NONE);
}
读块设备时,将连续的扇区分割成连续的条带,并根据磁盘数量确定条带所在的磁盘号,从相应的磁盘中读出。写设备时与此类似,确定条带号和磁盘号后,写入相应的磁盘。
RAID 0 类型磁盘阵列块设备的删除函数接口如下:
#include <SylixOS.h>
INT API_DiskRiad0Delete (PLW_BLK_DEV pblkDiskRaid);
函数 API_DiskRiad0Delete 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pblkDiskRaid 是之前创建的 RAID 0 虚拟磁盘。
RAID 1
RAID 1 称为磁盘镜像(Mirroring),一个具有全冗余的模式,如下图所示。RAID 1 可以用于两个或 2xN 个磁盘,并使用 0 块或更多的备用磁盘,每次写数据时会同时写入镜像盘。这种阵列可靠性很高,但其有效容量减小到总容量的一半,同时这些磁盘的大小应该相等,否则总容量只具有最小磁盘的大小。
在 SylixOS 中创建 RAID 1 类型磁盘阵列块设备的函数接口如下:
#include <SylixOS.h>
ULONG API_DiskRiad1Create (PLW_BLK_DEV pblkd[],
UINT uiNDisks,
PLW_BLK_DEV *ppblkDiskRaid);
函数 API_DiskRiad0Create 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误码。
- 参数 pblkd[] 是物理块设备列表。
- 参数 uiNDisks 是物理磁盘数量,只能为 2 块或者 4 块,二者选其一。
- 参数 ppblkDiskRaid 是返回的 RAID 虚拟磁盘。
API_DiskRiad1Create 函数将多个物理磁盘合并成一个更大的虚拟设备,阵列中所有的物理磁盘参数必须完全一致,例如磁盘大小、扇区大小等参数必须相同。函数中最终返回的 RAID 虚拟磁盘设备类型使用 LW_DISKRAID1_CB 结构描述,其具体结构如下:
typedef struct {
LW_BLK_DEV DISKR_blkdRaid; /* DISK CACHE 的 BLK IO 控制块 */
PLW_BLK_DEV *DISKR_ppblkdDisk; /* 阵列物理磁盘列表 */
UINT DISKR_uiNDisks; /* 阵列磁盘数量 */
} LW_DISKRAID1_CB;
typedef LW_DISKRAID1_CB *PLW_DISKRAID1_CB;
磁盘 shift 和磁盘掩码都与组成阵列的磁盘数量有关,RAID 阵列的 BLK IO 控制块为最终向上表现出的虚拟设备,其中的读接口函数如下:
static INT __raid1DevRd (PLW_DISKRAID1_CB pdiskr,
VOID *pvBuffer,
ULONG ulStartSector,
ULONG ulSectorCount)
{
INT i;
INT iRet = ERROR_NONE;
for (i = 0; i < pdiskr->DISKR_uiNDisks; i++) {
if (RAID_BLK_READ(pdiskr, i, pvBuffer, ulStartSector, ulSectorCount)) {
_DebugFormat(__ERRORMESSAGE_LEVEL,
"RAID-1 system block disk %u error.\r\n", i);
iRet = PX_ERROR;
} else {
break;
}
}
return (iRet);
}
读块设备时,只要从任意一个磁盘中读出即可。与此对应,RAID 1 的写操作需要对每一个磁盘进行相同的写操作。
RAID 1 类型磁盘阵列块设备的删除函数接口如下:
#include <SylixOS.h>
INT API_DiskRiad1Delete (PLW_BLK_DEV pblkDiskRaid);
函数 API_DiskRiad0Delete 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pblkDiskRaid 是之前创建的 RAID 1 虚拟磁盘。
此外 SylixOS 还提供了 RAID 1 的磁盘拷贝接口,用于备份数据或从镜像磁盘上恢复数据。其函数原型如下:
#include <SylixOS.h>
ULONG API_DiskRiad1Ghost (PLW_BLK_DEV pblkDest,
PLW_BLK_DEV pblkSrc,
ULONG ulStartSector,
ULONG ulSectorNum);
函数 API_DiskRiad1Ghost 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pblkDest 是目的磁盘。
- 参数 pblkSrc 是源磁盘。
- 参数 ulStartSector 是起始扇区。
- 参数 ulSectorNum 是扇区数量。