[toc]
include/linux/cache.h: 提供与CPU缓存行对齐相关的宏,如 ____cacheline_aligned __cacheline_aligned 缓存行对齐
这个修饰符确保变量在内存中对齐到缓存行的边界上。缓存行对齐可以减少缓存行冲突,提高缓存命中率,从而提高系统性能。在多处理器系统中,缓存行对齐尤为重要,因为多个处理器可能同时访问相同的内存区域。通过对齐,可以减少缓存一致性协议带来的开销。
在多处理器系统中,缓存行对齐可以减少缓存一致性协议带来的开销,主要原因如下:
缓存一致性协议:在多处理器系统中,每个处理器都有自己的缓存,用于存储频繁访问的数据。为了确保所有处理器看到的数据是一致的,系统使用缓存一致性协议(如MESI协议)。当一个处理器修改了某个缓存行中的数据,其他处理器必须更新或失效其缓存中的相应数据。这种一致性维护会带来额外的开销。
缓存行对齐的好处
减少伪共享:伪共享(False Sharing)是指多个处理器访问同一个缓存行中的不同数据项,导致频繁的缓存一致性操作。通过缓存行对齐,可以确保每个处理器访问的数据项位于不同的缓存行中,从而减少伪共享的发生。例如,如果两个处理器分别访问两个变量,而这两个变量恰好位于同一个缓存行中,那么每次一个处理器修改其中一个变量时,另一个处理器的缓存行也会失效,导致频繁的缓存一致性操作。通过对齐,可以将这两个变量放在不同的缓存行中,避免这种情况。
提高缓存命中率:缓存行对齐可以确保数据项在缓存中的位置是有序和连续的,从而提高缓存命中率。高缓存命中率意味着处理器可以更频繁地从缓存中读取数据,而不是从主内存中读取,从而提高系统性能。当数据项对齐到缓存行边界时,处理器可以更高效地预取和访问这些数据,减少缓存未命中的情况。
优化内存访问:缓存行对齐可以优化内存访问模式,使得内存访问更加高效。处理器可以更高效地加载和存储对齐的数据,从而减少内存访问的延迟。对齐的数据可以更好地利用处理器的缓存预取机制,减少内存访问的延迟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #ifndef __cacheline_aligned #define __cacheline_aligned \ __attribute__((__aligned__(SMP_CACHE_BYTES), \ __section__(".data..cacheline_aligned" ))) #endif #ifndef __cacheline_aligned_in_smp #ifdef CONFIG_SMP #define __cacheline_aligned_in_smp __cacheline_aligned #else #define __cacheline_aligned_in_smp #endif #endif
__ro_after_init 只读数据 1 2 3 4 5 6 7 8 #ifndef __ro_after_init #define __ro_after_init __section(".data..ro_after_init" ) #endif
____cacheline_internodealigned_in_smp 在多处理器(SMP)系统中对数据结构进行缓存线对齐 这段代码定义了一个宏 ____cacheline_internodealigned_in_smp
,用于在多处理器(SMP)系统中对数据结构进行缓存线对齐,以优化性能。以下是对这段代码的详细解释:
1 __attribute__((__aligned__(1 << (INTERNODE_CACHE_SHIFT))))
__attribute__((__aligned__(...)))
是 GCC 提供的一个扩展,用于指定变量或结构的对齐方式。
(1 << (INTERNODE_CACHE_SHIFT))
计算对齐的字节数,通常与缓存线的大小相关。INTERNODE_CACHE_SHIFT
是一个与缓存线大小相关的常量,表示对齐的位移量。
这种对齐方式确保数据结构在内存中与缓存线对齐,从而减少跨缓存线访问的开销,提高多核系统中的性能。
4. 非 SMP 系统
如果未启用 CONFIG_SMP
,宏 ____cacheline_internodealigned_in_smp
被定义为空:1 #define ____cacheline_internodealigned_in_smp
在单处理器系统中,缓存线对齐通常不是必要的,因为不存在多个 CPU 核心竞争缓存资源。
5. 用途
这个宏通常用于定义需要在多核系统中共享的全局变量或数据结构,例如锁、队列或统计信息。
在多核系统中,缓存线对齐可以减少伪共享(false sharing)的发生。伪共享是指多个 CPU 核心访问同一个缓存线中的不同数据时,导致不必要的缓存一致性操作,从而降低性能。
6. 总结 这段代码通过条件编译,根据系统是否支持 SMP 来决定是否对数据结构进行缓存线对齐。在多核系统中,这种对齐方式可以显著提高性能,而在单核系统中则避免了不必要的内存开销。这种设计兼顾了性能优化和资源节约,是内核开发中常见的模式。
include/linux/prefetch.h: 提供 prefetch() 宏,用于向CPU发出预取数据到缓存的指令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
prefetch 预取 “x” 处的 cacheline 进行读取 1 2 3 #ifndef ARCH_HAS_PREFETCH #define prefetch(x) __builtin_prefetch(x) #endif
arch/arm/include/asm/processor.h
pld
是 ARM 汇编指令,用于预取数据到缓存中。它的作用是将指定地址的数据预取到 CPU 的 L1 缓存中,以提高后续访问的速度。
pldw
是 ARM 汇编指令,用于预取数据到缓存中,并且它会将数据标记为“写入”。这意味着预取的数据可以在后续操作中被修改。
__ALT_SMP_ASM
是一个宏,用于在单处理器和多处理器系统之间切换不同的汇编指令。它的作用是根据系统的配置选择适当的汇编指令。
pld\t%a0
是 ARM 架构的预取指令,其中 pld
表示预取数据(Preload Data)。%a0
是一个占位符,表示将寄存器或内存地址替换为 ptr
的值。
“p” (ptr) 告诉编译器将 ptr 作为一个指针操作数传递给汇编代码。编译器会根据需要将其转换为适当的格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #if __LINUX_ARM_ARCH__ >= 5 #define ARCH_HAS_PREFETCH static inline void prefetch (const void *ptr) { __asm__ __volatile__( "pld\t%a0" :: "p" (ptr)); } #if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) #define ARCH_HAS_PREFETCHW static inline void prefetchw (const void *ptr) { __asm__ __volatile__( ".arch_extension mp\n" __ALT_SMP_ASM( "pldw\t%a0" , "pld\t%a0" ) :: "p" (ptr)); } #endif #endif
为什么只有SMP才能支持这个函数prefetchw
因为在多处理器系统中,缓存一致性是一个重要问题。pldw 指令不仅预取数据,还可以帮助处理器在写入数据时更高效地处理缓存一致性问题。 如果系统是单处理器(非 SMP),则没有必要使用 pldw 指令,因为缓存一致性问题在单核环境中并不存在。因此,在非 SMP 环境中,prefetchw 函数会降级为普通的 pld 指令(只读预取)。
prefetchw 预取 “x” 处的 cacheline 进行写入 1 2 3 #ifndef ARCH_HAS_PREFETCHW #define prefetchw(x) __builtin_prefetch(x,1) #endif
include/linux/poison.h: 为调试目的,定义用于填充已释放内存的“毒药”值
安全性目的:
通过将毒指针指向不可映射的内存区域,可以防止用户空间的恶意代码利用这些指针进行攻击。
这种设计增强了系统的安全性,特别是在防止用户空间漏洞方面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #ifdef CONFIG_ILLEGAL_POINTER_VALUE # define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL) #else # define POISON_POINTER_DELTA 0 #endif #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x122 + POISON_POINTER_DELTA) #define TIMER_ENTRY_STATIC ((void *) 0x300 + POISON_POINTER_DELTA) #define PAGE_POISON 0xaa #define TAIL_MAPPING ((void *) 0x400 + POISON_POINTER_DELTA) #define SLUB_RED_INACTIVE 0xbb #define SLUB_RED_ACTIVE 0xcc #define POISON_INUSE 0x5a #define POISON_FREE 0x6b #define POISON_END 0xa5 #define POISON_FREE_INITMEM 0xcc #define JBD_POISON_FREE 0x5b #define JBD2_POISON_FREE 0x5c #define POOL_POISON_FREED 0xa7 #define POOL_POISON_ALLOCATED 0xa9 #define ATM_POISON_FREE 0x12 #define ATM_POISON 0xdeadbeef #define MUTEX_DEBUG_INIT 0x11 #define MUTEX_DEBUG_FREE 0x22 #define MUTEX_POISON_WW_CTX ((void *) 0x500 + POISON_POINTER_DELTA) #define KEY_DESTROY 0xbd #define PP_SIGNATURE (0x40 + POISON_POINTER_DELTA) #define SKB_LIST_POISON_NEXT ((void *)(0x800 + POISON_POINTER_DELTA)) #define NET_PTR_POISON ((void *)(0x801 + POISON_POINTER_DELTA)) #define BPF_PTR_POISON ((void *)(0xeB9FUL + POISON_POINTER_DELTA)) #define VFS_PTR_POISON ((void *)(0xF5 + POISON_POINTER_DELTA)) #define STACK_DEPOT_POISON ((void *)(0xD390 + POISON_POINTER_DELTA))
include/linux/uaccess.h: (内存标记) 提供内核空间与用户空间之间安全拷贝数据的核心函数,如 copy_to_user() user_access_save user_access_restore
user_access_save
和 user_access_restore
是两个宏,用于在内核代码中处理用户空间访问的上下文保存和恢复。这些宏通常用于在内核中进行用户空间访问时,确保正确的上下文切换和权限检查。仅有特定的架构支持这些宏。
user_access_save
用于保存当前的用户空间访问状态,以便在稍后恢复。它通常在内核代码中用于保护对用户空间内存的访问。
user_access_restore
用于恢复之前保存的用户空间访问状态。这通常在完成对用户空间内存的访问后调用,以确保内核能够正确地恢复到之前的状态。
1 2 static inline unsigned long user_access_save (void ) { return 0UL ; }static inline void user_access_restore (unsigned long flags) { }
include/linux/mmdebug.h: 提供内存管理子系统的调试宏和断言 1 2 3 4 5 #ifdef CONFIG_DEBUG_VM #define VM_BUG_ON(cond) BUG_ON(cond) #else #define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond) #endif