首页 > 代码库 > SyilxOS块设备CACHE管理

SyilxOS块设备CACHE管理

  • 1.块设备CACHE简介

         前文介绍了SylixOS中的块设备驱动模型和I/O控制,本篇主要介绍SylixOS中的块设备CACHE管理。由于磁盘属于低速设备,磁盘的读写速度远远低于CPU,所以为了解决这种速度不匹配,SylixOS提供了对应块设备的缓冲器。它是一个特殊的块设备,与物理设备一一对应(多个逻辑分区共享一个CACHE),介于文件系统和磁盘之间,可以极大地减少磁盘I/O的访问率,同时提高系统性能。当然引入磁盘缓冲器的最大问题在于磁盘数据与缓冲数据的不同步性,这个问题可以通过sync等函数调用来同步数据。

  • 2. 技术实现

        SylixOS对块设备CACHE管理的代码在"libsylixos/SylixOS/fs/diskCache"目录下,主要包含磁盘缓冲器的创建、删除和同步操作,具体接口如程序清单 2-1所示。

    程序清单 2-1 CACHE管理接口

    LW_API ULONG  API_DiskCacheCreate(PLW_BLK_DEV   pblkdDisk, 
                                      PVOID         pvDiskCacheMem, 
                                      size_t        stMemSize, 
                                      INT           iMaxBurstSector,
                                      PLW_BLK_DEV  *ppblkDiskCache);
    LW_API ULONG  API_DiskCacheCreateEx(PLW_BLK_DEV   pblkdDisk, 
                                        PVOID         pvDiskCacheMem, 
                                        size_t        stMemSize, 
                                        INT           iMaxRBurstSector,
                                        INT           iMaxWBurstSector,
                                        PLW_BLK_DEV  *ppblkDiskCache);
    LW_API ULONG  API_DiskCacheCreateEx2(PLW_BLK_DEV          pblkdDisk, 
                                         PLW_DISKCACHE_ATTR   pdcattrl,
                                         PLW_BLK_DEV         *ppblkDiskCache);
    LW_API INT    API_DiskCacheDelete(PLW_BLK_DEV   pblkdDiskCache);
    LW_API INT    API_DiskCacheSync(PLW_BLK_DEV   pblkdDiskCache);

        函数API_DiskCacheCreate是用来创建一个磁盘CACHE块设备,pblkdDisk为一个指向需要CACHE的块设备结构的指针。该函数会调用到API_DiskCacheCreateEx函数,而API_DiskCacheCreateEx函数会继续调用API_DiskCacheCreateEx2函数。三者在调用中对CACHE信息不断封装完善。

    函数API_DiskCacheDelete用来删除一个磁盘CACHE块设备,在删除磁盘之前首先要卸载卷。

    函数API_DiskCacheSync用来磁盘高速缓冲控制器的回写。

  • 2.1 块设备CACHE创建

        当使用函数API_DiskCacheCreate创建一个磁盘CACHE块设备时,用户需要提供磁盘CACHE的各种属性信息,如CACHE缓冲区的内存起始地址、缓冲区大小、磁盘猝发读写的最大扇区数等。创建函数对属性信息进行封装,添加各种默认配置,最终调用API_DiskCacheCreateEx2函数进行具体的创建操作。用户也可自己直接封装信息描述结构体,并调用API_DiskCacheCreateEx2函数进行CACHE创建,封装后的磁盘CACHE描述结构体如程序清单 2-2所示。

    程序清单 2-2 CACHE描述信息结构体

    typedef struct {
        PVOID           DCATTR_pvCacheMem;                /*  扇区缓存地址                */
        size_t          DCATTR_stMemSize;                 /*  扇区缓存大小                */
        BOOL            DCATTR_bCacheCoherence;           /*  缓冲区需要 CACHE 一致性保障 */
        INT             DCATTR_iMaxRBurstSector;          /*  磁盘猝发读的最大扇区数      */
        INT             DCATTR_iMaxWBurstSector;          /*  磁盘猝发写的最大扇区数      */
        INT             DCATTR_iMsgCount;                 /*  管线消息队列缓存个数        */
        INT             DCATTR_iPipeline;                 /*  处理管线线程数量            */
        BOOL            DCATTR_bParallel;                 /*  是否支持并行读写            */
        ULONG           DCATTR_ulReserved[8];             /*  保留                        */
    } LW_DISKCACHE_ATTR;
    typedef LW_DISKCACHE_ATTR  *PLW_DISKCACHE_ATTR;

        当设备支持并发读写时操作时,DCATTR_bParallel等于LW_TURE,否则为LW_FALSE。DCATTR_iPipeline大于等于0且 小于 LW_CFG_DISKCACHE_MAX_PIPELINE。 DCATTR_iPipeline 等于0 表示不使用管线并发技术。DCATTR_iMsgCount 最小为 DCATTR_iPipeline,可以为 DCATTR_iPipeline 2 ~ 8 倍。关于磁盘高速传输中使用的并发写管线将在后续章节中介绍,本文不做过多说明。

        每个磁盘CACHE块设备由一个控制块管理,该控制块包含了各种控制信息,具体结构如程序清单 2-3所示。

    程序清单 2-3 CACHE控制块结构体

    typedef struct {
        LW_BLK_DEV              DISKC_blkdCache;          /*  DISK CACHE 的 BLK IO 控制块 */
        PLW_BLK_DEV             DISKC_pblkdDisk;          /*  被缓冲 BLK IO 控制块地址    */
        LW_LIST_LINE            DISKC_lineManage;         /*  背景线程管理链表            */
        
        LW_OBJECT_HANDLE        DISKC_hDiskCacheLock;     /*  DISK CACHE 操作锁           */
        INT                     DISKC_iCacheOpt;          /*  CACHE 工作选项              */
        
        ULONG                   DISKC_ulEndStector;       /*  最后一个扇区的编号          */
        ULONG                   DISKC_ulBytesPerSector;   /*  每一扇区字节数量            */
        
        ULONG                   DISKC_ulValidCounter;     /*  有效的扇区数量              */
        ULONG                   DISKC_ulDirtyCounter;     /*  需要回写的扇区数量          */
        
        INT                     DISKC_iMaxRBurstSector;   /*  最大猝发读写扇区数量        */
        INT                     DISKC_iMaxWBurstSector;
        LW_DISKCACHE_WP         DISKC_wpWrite;            /*  并发写管线                  */
        
        PLW_LIST_RING           DISKC_pringLruHeader;     /*  LRU 表头                    */
        PLW_LIST_LINE          *DISKC_pplineHash;         /*  HASH 表池                   */
        INT                     DISKC_iHashSize;          /*  HASH 表大小                 */
        
        ULONG                   DISKC_ulNCacheNode;       /*  CACHE 缓冲的节点数          */
        caddr_t                 DISKC_pcCacheNodeMem;     /*  CACHE 节点链表首地址        */
        caddr_t                 DISKC_pcCacheMem;         /*  CACHE 缓冲区                */
        PLW_DISKCACHE_NODE      DISKC_disknLuck;          /*  幸运扇区节点                */
        
        VOIDFUNCPTR             DISKC_pfuncFsCallback;    /*  文件系统回调函数            */
        PVOID                   DISKC_pvFsArg;            /*  文件系统回调参数            */
    } LW_DISKCACHE_CB;
    typedef LW_DISKCACHE_CB    *PLW_DISKCACHE_CB;

        创建的过程主要包括:

           1.创建回写锁和背景回写线程

           2.创建/proc中的文件节点

           3.初始化磁盘

           4.开辟控制块内存

           5.创建操作互斥信号量和并发写队列

           6.创建HASH表池

           7.开辟缓冲内存池

           8.初始化BLK_DEV

           9.DISK CACHE块加入背景线程

  •     完成初始化后,CACHE块设备以CACHE节点管理着每个扇区,节点通过双向链表构成一个LRU循环链表,最新使用的一般插入到表头,最久未用或空节点一般在表尾。当节点被使用时,根据缓冲的扇区号将节点加入到对应的HASH表链表中,方便下次查找时快速命中,详细内容如程序清单 2-4所示。

    程序清单 2-4 CACHE节点结构体

    typedef struct {
        LW_LIST_RING            DISKN_ringLru;           /*  LRU 表节点                  */
        LW_LIST_LINE            DISKN_lineHash;          /*  HASH 表节点                 */
        
        ULONG                   DISKN_ulSectorNo;        /*  缓冲的扇区号                */
        INT                     DISKN_iStatus;           /*  节点状态                    */
        caddr_t                 DISKN_pcData;            /*  扇区数据缓冲                */
        
        UINT64                  DISKN_u64FsKey;          /*  文件系统自定义数据          */
    } LW_DISKCACHE_NODE;
    typedef LW_DISKCACHE_NODE  *PLW_DISKCACHE_NODE;
  • 2.2 磁盘CACHE块设备删除

        磁盘CACHE块设备删除接口为API_DiskCacheDelete函数,删除时需要等待所有写操作完成后,删除写管线、退出背景线程、删除信号量、释放内存。

  • 2.3 磁盘CACHE块设备同步

        磁盘CACHE块设备的回写有多种接口,包括将指定扇区范围回写,将指定关键区回写或将指定扇区数量回写。此处以将磁盘CACHE的指定扇区数量回写为例,回写时即查找LRU表中最后的若干数量的节点,若为脏节点则插入到一个按照缓冲扇区号升序排列的链表中,同时将其从LRU表中删除,若为无效节点,则直接从有效的HASH表中删除。

        将有序链表中的CACHE节点全部回写磁盘时,会先检查是否为连续扇区,若为连续扇区则一次写请求可写多个扇区,否则一次回写一个扇区。若磁盘缓冲器不支持多管线,则直接调用磁盘物理设备的写接口,而多管线线程下,只需发送一个消息到写管线线程,在写管线线程中进行具体的写操作。完成一个节点的回写后,需要将节点从临时回写链表中删除,并加入到磁盘CACHE的LRU链表结尾。

          磁盘CACHE块设备同步接口API_DiskCacheSync通过调用ioctl的FIOFLUSH实现将所有的缓冲全部回写 ,操作流程与前述相似。

  • 2.4 磁盘CACHE块设备读写

        当文件系统需要进行读写操作时,最终会调用到磁盘对应的CACHE块设备读写操作。此时,需要先获取一个经过指定处理的CACHE节点。首先根据扇区号从有效的HASH表中查找,若找到则直接命中,否则开辟新的节点。创建一个CACHE块时,若所有CACHE全部为脏,或是无法在LRU表中找到合适的控制块,则需要做FLUSH操作,回写一些扇区的数据,之后重新查找。查找时从LRU表的结尾向前查找,过程中若发现无效扇区,需要从有效表中删除。查找完成后,将找到的节点置为有效,并加入到相关的HASH表中。若为读操作,根据是否为直接命中,选择是否需要从磁盘中读取数据到CACHE块中,若为写数据,则将CACHE块设置脏位标记。最后,将该节点设置为幸运节点,并从LRU表中重新设置到表头。

        在获取到CACHE节点后,根据不同的操作,向CACHE中写入数据,或从中读取数据。

  • 3. 参考资料

        无

SyilxOS块设备CACHE管理