磁盘高速传输
SylixOS 管线模型分析
SyilxOS 在 CAHCE 回写的过程采取了两种传输方式,即直接回写和多管线并发回写。并发写管线通过多线程并发处理 CACHE 提交的写请求,实现一定意义上的磁盘高速传输。SylixOS 中通过 LW_DISKCACHE_WP 结构体管理并发写管线,该结构体的具体内容如下:
typedef struct {
BOOL DISKCWP_bExit; /* 是否需要退出 */
BOOL DISKCWP_bCacheCoherence; /* CACHE 一致性标志 */
BOOL DISKCWP_bParallel; /* 并行化读写支持 */
INT DISKCWP_iPipeline; /* 写管线线程数 */
INT DISKCWP_iMsgCount; /* 写消息缓冲个数 */
PVOID DISKCWP_pvRBurstBuffer; /* 管线缓存 */
PVOID DISKCWP_pvWBurstBuffer; /* 管线缓存 */
LW_OBJECT_HANDLE DISKCWP_hMsgQueue; /* 管线刷新队列 */
LW_OBJECT_HANDLE DISKCWP_hCounter; /* 计数信号量 */
LW_OBJECT_HANDLE DISKCWP_hPart; /* 管线缓存管理 */
LW_OBJECT_HANDLE DISKCWP_hSync; /* 排空信号 */
LW_OBJECT_HANDLE DISKCWP_hDev; /* 非并发设备锁 */
LW_OBJECT_HANDLE DISKCWP_hWThread[LW_CFG_DISKCACHE_MAX_PIPELINE]; /* 管线写任务表 */
} LW_DISKCACHE_WP;
typedef LW_DISKCACHE_WP *PLW_DISKCACHE_WP;
- DISKCWP_bExit :为 LW_TRUE 时,写管线线程将会退出。
- DISKCWP_bCacheCoherence :为 LW_TRUE 时,管线缓存将使用非缓冲的内存。
- DISKCWP_bParallel :并行化读写支持,如果不支持则需要在操作设备前调用设备锁,防止并发操作。
- DISKCWP_iPipeline :写管线线程数,为 0 时表示不使用并发写管线。
- DISKCWP_iMsgCount :写消息缓冲个数,最小为 DCATTR_iPipeline,可以为 DCATTR_iPipeline 的 2 ~ 8 倍。
- DISKCWP_hMsgQueue :管线刷新队列,上层通过发送一个消息,发起一个写请求。
- DISKCWP_hCounter :计数信号量,用于计数当前缓冲块,发起写请求时申请,完成回写时释放。
- DISKCWP_hPart :通过定长内存管理管线缓存。
- DISKCWP_hDev :非并发设备锁。
- DISKCWP_hWThread :管线写任务表。
- DISKCWP_hSync :排空信号,用于回写完成后同步。
写管线的创建通过调用 __diskCacheWpCreate 函数来完成,其函数原型如下:
#include <SylixOS.h>
INT __diskCacheWpCreate(PLW_DISKCACHE_CB pdiskc,
PLW_DISKCACHE_WP pwp,
BOOL bCacheCoherence,
BOOL bParallel,
INT iPipeline,
INT iMsgCount,
INT iMaxRBurstSector,
INT iMaxWBurstSector,
ULONG ulBytesPerSector);
函数 __diskCacheWpCreate 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pdiskc 是磁盘缓冲控制块。
- 参数 pwb 是并发写管线控制块。
- 参数 bCacheCoherence 是 CACHE 一致性需求。
- 参数 bParallel 是表明并发读写支持。
- 参数 iPipeline 是写管线线程数。
- 参数 iMsgCount 是管线总消息个数。
- 参数 iMaxRBurstSector 是读猝发长度。
- 参数 iMaxWBurstSector 是写猝发长度。
- 参数 ulBytesPerSector 是每扇区大小。
函数 __diskCacheWpCreate 根据入参创建对应的写管线,并填充相关信息到并发写管线控制结构体。写管线线程运行后,循环等待接收管线刷新消息。当线程接收到消息后,根据消息中的信息调用具体的硬件接口进行写操作,释放相关资源后进入下一次循环等待接收消息。其中消息类型如下:
typedef struct {
ULONG DISKCWPM_ulStartSector; /* 起始扇区 */
ULONG DISKCWPM_ulNSector; /* 扇区数量 */
PVOID DISKCWPM_pvBuffer; /* 扇区缓冲 */
} LW_DISKCACHE_WPMSG;
typedef LW_DISKCACHE_WPMSG *PLW_DISKCACHE_WPMSG;
其中的扇区缓冲通过定长内存分区管理,分区总大小为:最大写猝发大小 × 消息个数,每个内存块的大小为最大写猝发的大小。上层发起写请求时,需要从定长分区中申请内存块,将要处理的数据填入缓冲区。为了实现互斥,系统还创建了初始值与消息个数相等的计数信号量,每次申请内存块前,都需要先申请信号量。所以管线线程完成写操作后,同时要释放消息中的内存块以及计数信号量和同步信号。
SylixOS 管线使用
SylixOS 管线使用主要在 CACHE 回写时,即调用 diskCacheFlushList 函数将链表内的 CACHE 节点扇区全部回写磁盘。由于链表以扇区号升序排列,所以能够方便的查找连续扇区,进行多扇区猝发写操作。此时,需要调用 diskCacheWpGetBuffer 函数在已初始化后的内存分区中申请一个内存块,该函数的具体实现如下:
PVOID __diskCacheWpGetBuffer (PLW_DISKCACHE_WP pwp, BOOL bRead)
{
PVOID pvRet;
if (bRead) {
return (pwp->DISKCWP_pvRBurstBuffer);
}
if (pwp->DISKCWP_iPipeline == 0) {
return (pwp->DISKCWP_pvWBurstBuffer);
}
if (API_SemaphoreCPend(pwp->DISKCWP_hCounter, LW_OPTION_WAIT_INFINITE)) {
_BugHandle(LW_TRUE, LW_TRUE, "diskcache pipeline error!\r\n");
}
pvRet = API_PartitionGet(pwp->DISKCWP_hPart);
_BugHandle((pvRet == LW_NULL), LW_TRUE, "diskcache pipeline error!\r\n");
return (pvRet);
}
在申请内存块前,需要先请求计数信号量,计数信号量与内存块数量相等。当内存分区中已没有剩余的内存块时,线程无法获得计数信号量进入休眠。当管线线程完成写操作后会释放接收到的内存块,并释放计数信号量,此时休眠线程成功申请信号量进入就绪态,并顺利获得内存块。
接着需要将 CACHE 中的缓冲数据拷贝到内存块中,并提交一个写请求。管线线程接收到消息后进行具体的写操作和资源释放。写请求函数具体实现如下:
INT __diskCacheWpWrite (PLW_DISKCACHE_CB pdiskc,
PLW_BLK_DEV pblkdDisk,
PVOID pvBuffer,
ULONG ulStartSector,
ULONG ulNSector)
{
LW_DISKCACHE_WPMSG diskcwpm;
PLW_DISKCACHE_WP pwp = &pdiskc->DISKC_wpWrite;
if (pwp->DISKCWP_iPipeline == 0) {
return (pdiskc->DISKC_pblkdDisk->BLKD_pfuncBlkWrt(pblkdDisk,
pvBuffer,
ulStartSector,
ulNSector));
}
diskcwpm.DISKCWPM_ulStartSector = ulStartSector;
diskcwpm.DISKCWPM_ulNSector = ulNSector;
diskcwpm.DISKCWPM_pvBuffer = pvBuffer;
API_MsgQueueSend2(pwp->DISKCWP_hMsgQueue, &diskcwpm,
sizeof(LW_DISKCACHE_WPMSG), LW_OPTION_WAIT_INFINITE);
return (ERROR_NONE);
}
发起写请求后可通过调用 __diskCacheWpSync 函数进行写同步,该函数通过写管线控制块中的 DISKCWP_hSync 信号量实现同步功能。