🌟🌟🌟博主主页:MuggleZero🌟🌟🌟
《ARMv8架构初学者笔记》专栏地址:《ARMv8架构初学者笔记》
前文:
【Cache篇】初见Cache
【Cache篇】Cache的映射方式
【Cache篇】包容性和排他性的Cache
【Cache篇】Cache的分类
【Cache篇】MESI协议
【Cache篇】Cache伪共享
【Cache篇】DMA和Cache的一致性
一般来说,cache line的大小都很小(典型值32字节)。CPU的cache时线性排列的,也就是说对于32字节的cache line是与32字节的地址对齐的。
cache在Linux内核中有着广泛且巧妙的应用,接下来我们看看都有哪些妙用。
常用数据结构对齐
在proc_caches_init中,mm_struct,fs_cache,files_cache和signal_cache等结构体都通过标志位SLAB_HWCACHE_ALIGN创建了slab描述符并且与L1 cache进行了对齐。
如何对齐呢?
我们先以mm_struct进行举例分析,kmem_cache_create_usercopy创建的cache对象是可以拷贝到用户层的。通过是否传入usersize来进行区分调用流程,显然上面传入了usersize。
kmem_cache_create_usercopy
如果传入了usersize,那么就需要计算对齐的大小,calculate_alignment主要针对硬件缓存的对齐方式不能覆盖指定的对齐方式。
cache_line_size通过读取SYS_CTR_EL0这个寄存器来获取CPU的L1 cache大小,获取失败会使用ARM64中DMA最小的对齐大小128字节。
#define ARCH_DMA_MINALIGN (128)static inline int cache_line_size_of_cpu(void){u32 cwg = cache_type_cwg();return cwg ? 4 << cwg : ARCH_DMA_MINALIGN;}static inline u32 cache_type_cwg(void){return (read_cpuid_cachetype() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;}static inline u32 __attribute_const__ read_cpuid_cachetype(void){return read_cpuid(CTR_EL0);}#define read_cpuid(reg) read_sysreg_s(SYS_ ## reg)
获得正确的对齐大小后使用create_cache->__kmem_cache_create()创建一个对齐的cache对象。
kmem_cache_create
kmem_cache_create调用kmem_cache_create_usercopy并传入空的usersize。因此只会调用到__kmem_cache_alias->find_mergeable。find_mergeable核心思想也是使用calculate_alignment先计算对齐的大小,然后去系统的slab caches链表中寻找一个可合并的slab缓存。
埋点:具体slab系统是如何创建的后面再讲。
cache和内存交换的最小单位就是cache line,如果结构体没有与cache line对齐,那么一个结构体很有可能占用了多个cache line,导致性能下降!
SMP系统中的对齐
针对SMP系统,一些常用的数据结构(zone,irqaction,irq_stat,worker_pool)在定义时就使用了cacheline_aligned_in_smp和cacheline_internodealigned_in_smp等宏来定义数据结构。之前提到的cache伪共享问题在SMP系统中会有很大的影响,解决它的办法是让结构体按照cache line进行对齐,例如Linux中按照L1_CACHE_BYTES对齐。
#ifndef L1_CACHE_ALIGN#define L1_CACHE_ALIGN(x) __ALIGN_KERNEL(x, L1_CACHE_BYTES)#endif#ifndef SMP_CACHE_BYTES#define SMP_CACHE_BYTES L1_CACHE_BYTES#endif#ifndef ____cacheline_aligned#define ____cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES)))#endif#ifndef ____cacheline_aligned_in_smp#ifdef CONFIG_SMP#define ____cacheline_aligned_in_smp ____cacheline_aligned#else#define ____cacheline_aligned_in_smp#endif /* CONFIG_SMP */#endif#ifndef __cacheline_aligned#define __cacheline_aligned \__attribute__((__aligned__(SMP_CACHE_BYTES),\__section__(".data..cacheline_aligned")))#endif /* __cacheline_aligned */#ifndef __cacheline_aligned_in_smp#ifdef CONFIG_SMP#define __cacheline_aligned_in_smp __cacheline_aligned#else#define __cacheline_aligned_in_smp#endif /* CONFIG_SMP */#endif#if !defined(____cacheline_internodealigned_in_smp)#if defined(CONFIG_SMP)#define ____cacheline_internodealigned_in_smp \__attribute__((__aligned__(1 << (INTERNODE_CACHE_SHIFT))))#else#define ____cacheline_internodealigned_in_smp#endif#endif#ifndef CONFIG_ARCH_HAS_CACHE_LINE_SIZE#define cache_line_size() L1_CACHE_BYTES#endif
独占cache line
对于数据结构中频繁访问的成员我们可以设置它独占cache line。为啥要让它独占呢,还是cache伪缓存问题,这个成员可能导致互相干架,频繁导入导出cache line。例如zone->lock和zone->lru_lock这两个频繁的锁,有助于提高获取锁的效率。在SMP系统中,自旋锁的争用会导致严重的cache line颠簸现象。
欢迎关注我的个人微信公众号,一起交流学习嵌入式开发知识!
关注「求密勒实验室」