乐趣区

关于cpu:嵌入式系统文件系统开发缓存的应用

一、前言

最近开发一基于嵌入式零碎下的“文件系统”,前期测试呈现一些离奇的 BUG,经审查和排故发现是开启了缓存加上 DMA 搬运数据导致的 CACHE 不统一问题。当初就来分享一下。

二、缓存

改文件系统的利用场景是基于嵌入式操作系统,实现数据的无效记录。硬件设计基于 Z7,BSP 默认关上了 CPU 和主存之间的 cache, 且写数据形式为写贯通。利用运行中,CPU 通过 cache 拜访操作主存,而读写电子盘的驱动默认采纳 DMA 搬运数据到主存,这是 cache 不统一的根本原因。
本文先介绍一下几个概念:

  1. cache line
    CPU cache 的构造是由很多 Cache Line 组成的。每条 cache line 都有两个标记位。
  2. 无效位(valid bit)
    示意 cache line 的数据是否无效。零碎刚启动时,数据都置有效。
  3. 脏位 (dirty bit)
    示意 cache line 的数据是否和下一级缓存 统一。0 统一,1 不统一
  4. 命中(Hit)
    CPU 要拜访的数据在 Cache 中有缓存。成为命中(Hit),反之为缺失(Miss)
  5. DMA(Direct Memory Acess)
    间接存储器拜访,是一种不通过 CPU 而间接从内存存取数据的数据交换形式。否则,CPU 须要从起源把每一片段的材料复制到暂存器,而后把它们再次写回新的中央。

三、缓存的读写形式

  1. 写中转(write through)
    任一从 CPU 收回的写信号送到 cache 的同时,也写入主存,以保障主存的数据可能同步更新。
  2. 写回(write back)

    如果当产生写操作时,数据曾经在 CPU Cache 里的话,则把数据更新到 CPU Cache 里,同时标记 CPU Cache 里的这个 Cache Block 为脏 (Cache Block 的数据和内存是不统一);
    如果当产生写操作时,数据所对应的 Cache Block 里寄存的是「别的内存地址的数据」的话,就要检 查这个 Cache Block 里的数据有没有被标记为脏的,如果是脏的话,咱们就要把这个 Cache Block 里的数据写回到内存,而后再把以后要写入的数据,写入到这个 Cache Block 里,同时也把它标记为 脏的; 如果 Cache Block 外面的数据没有被标记为脏,则就间接将数据写入到这个 Cache Block 里,而后再把这个 Cache Block 标记为脏。

  1. 读贯通(read through)
    CPU 的所有对主存的数据申请都先送到 cache,如果命中,则不申请拜访主存,并将数据送出;如果不命中,则向主存申请数据。
  2. 读旁路(read aside)
    CPU 收回数据申请时,并不是单通道地穿过 Cache。而是向 Cache 和主存同时发出请求。因为 Cache 速度更快,如果命中,则 Cache 在将数据回送给 CPU 的同时,还来得及中断 CPU 对主存的申请;不命中。则 Cache 不做任何动作。由 CPU 间接拜访主存。

四、利用场景

文件系统是基于 vxWorks 开发的。针对 CACHE 不统一问题,剖析 vxWorks 零碎提供的 cacheLib,提出两种解决办法:

  1. 所有通过 DMA 操作的数据都用 cacheDmaMalloc 申请内存空间
    默认用 malloc 申请的内存不是缓存平安的。用 cacheDmaMalloc 能够为 DMA 设施和驱动调配缓存平安的内存缓冲。
  2. 调用 cacheFlush 和 cacheInvalidate 解决问题
    cacheFlush 强制将缓冲的数据更新到内存。对于写贯通类型,cacheFlush 什么都不须要做因为内存和缓存条目是匹配的。cacheInvalidate 将所有的缓冲条目都设置为有效,齐全切断内存和缓冲之间的分割。

本我的项目采纳的是第二种形式,间接在调用磁盘的读写驱动处减少 cacheFlush 和 cacheInvalidate。在调用写驱动之前,调用 cacheFlush 强制将缓冲的数据更新到内存,保障写入磁盘的数据是从 cache 中拿到的最新的数据。在调用读驱动后,调用 cacheInvalidate 切断以后内存区域和 cache 的分割,保障后续 CPU 拜访该区域的时候,可能间接拜访内存而不是缓存中可能存在的旧数据。

int rawFsBlkWrt(unsigned int startsector,int nsectors,char *pdata, const char *devname)
{
    STATUS stats = ERROR;
    BLK_DEV * pdev = NULL;
    int ldrs_num = -1;
    int i = 0;
    
    for(i=0;i<LDRS_NUM;i++)
    {if(ldrs_handle[i].ldrs_valid_flag == LDRS_HANDLE_VALID_FLAG)
        {if(0==strcmp(devname,ldrs_handle[i].ldrs_name))
            {
                ldrs_num = i;
                break;
            }
        }
    }
    if(ldrs_num<0)
    {
        ldrs_errno = ERR_BLKDEV_INVALID; 
        printf("ERR_BLKDEV_INVALID %s",(char *)devname);
        return -1;
    }

    if(pdata == NULL)
    {
        ldrs_errno = ERR_ADDR_IS_NULL;
        return ldrs_errno;
    }

    if(ERROR == semTake(ldrs_handle[ldrs_num].sem_blkdev,sysClkRateGet()*30))
    {
        ldrs_errno = ERR_SEMPHONE_TAKE;
        return ldrs_errno;
    }

    cacheFlush(DATA_CACHE, (void*)pdata, nsectors);

    stats = fsBlkWrt(pdev,startsector,nsectors,pdata);
    semGive(ldrs_handle[ldrs_num].sem_blkdev);
    if(stats == ERROR)
    {
        ldrs_errno = ERR_BLK_WRITE; 
        return ldrs_errno;
    }

    return DISC_OK;    
}
int rawFsBlkRd(unsigned int startsector,int nsectors,char *pdata, const char *devname)
{
    STATUS stats = ERROR;
    BLK_DEV * pdev = NULL;
    int ldrs_num = -1;
    int i = 0;
    for(i=0;i<LDRS_NUM;i++)
    {if(0==strcmp(devname,ldrs_handle[i].ldrs_name))
        {
        ldrs_num = i;
        break;
        }
    }
    if(ldrs_num<0)
    {
        ldrs_errno = ERR_BLKDEV_INVALID; 
        printf("ERR_BLKDEV_INVALID %s",(char *)devname);
        return -1;
    }

    if(pdata == NULL)
    {
        ldrs_errno = ERR_ADDR_IS_NULL;
        return ldrs_errno;
    }

    if(ERROR == semTake(ldrs_handle[ldrs_num].sem_blkdev,sysClkRateGet()*30))
    {
        ldrs_errno = ERR_SEMPHONE_TAKE;
        return ldrs_errno;
    }
    stats = fsBlkRd(pdev,startsector,nsectors,pdata);
    cacheInvalidate(DATA_CACHE, pdata, nsectors);
    semGive(ldrs_handle[ldrs_num].sem_blkdev);
    if(stats == ERROR)
    {
        ldrs_errno = ERR_BLK_READ; 
        return ldrs_errno;
    }

    return DISC_OK;    
}
退出移动版