一、前言
最近开发一基于嵌入式零碎下的“文件系统”,前期测试呈现一些离奇的BUG,经审查和排故发现是开启了缓存加上DMA搬运数据导致的CACHE不统一问题。当初就来分享一下。
二、缓存
改文件系统的利用场景是基于嵌入式操作系统,实现数据的无效记录。硬件设计基于Z7,BSP默认关上了CPU和主存之间的cache, 且写数据形式为写贯通。利用运行中,CPU通过cache拜访操作主存,而读写电子盘的驱动默认采纳DMA搬运数据到主存,这是cache不统一的根本原因。
本文先介绍一下几个概念:
- cache line
CPU cache的构造是由很多Cache Line组成的。每条cache line都有两个标记位。 - 无效位(valid bit)
示意cache line的数据是否无效。零碎刚启动时,数据都置有效。 - 脏位(dirty bit)
示意cache line的数据是否和下一级缓存统一。0统一,1不统一 - 命中(Hit)
CPU要拜访的数据在Cache中有缓存。成为命中(Hit),反之为缺失(Miss) - DMA(Direct Memory Acess)
间接存储器拜访,是一种不通过CPU而间接从内存存取数据的数据交换形式。否则,CPU须要从起源把每一片段的材料复制到暂存器,而后把它们再次写回新的中央。
三、缓存的读写形式
- 写中转(write through)
任一从CPU收回的写信号送到cache的同时,也写入主存,以保障主存的数据可能同步更新。 写回(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 标记为脏。
- 读贯通(read through)
CPU的所有对主存的数据申请都先送到cache,如果命中,则不申请拜访主存,并将数据送出;如果不命中,则向主存申请数据。 - 读旁路(read aside)
CPU收回数据申请时,并不是单通道地穿过Cache。而是向Cache和主存同时发出请求。因为Cache速度更快,如果命中,则Cache在将数据回送给CPU的同时,还来得及中断CPU对主存的申请;不命中。则Cache不做任何动作。由CPU间接拜访主存。
四、利用场景
文件系统是基于vxWorks开发的。针对CACHE不统一问题,剖析vxWorks零碎提供的cacheLib,提出两种解决办法:
- 所有通过DMA操作的数据都用cacheDmaMalloc申请内存空间
默认用malloc申请的内存不是缓存平安的。用cacheDmaMalloc能够为DMA设施和驱动调配缓存平安的内存缓冲。 - 调用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; }