[toc]

include/linux/cache.h: 提供与CPU缓存行对齐相关的宏,如 ____cacheline_aligned

__cacheline_aligned 缓存行对齐

  • 这个修饰符确保变量在内存中对齐到缓存行的边界上。缓存行对齐可以减少缓存行冲突,提高缓存命中率,从而提高系统性能。在多处理器系统中,缓存行对齐尤为重要,因为多个处理器可能同时访问相同的内存区域。通过对齐,可以减少缓存一致性协议带来的开销。
    • 在多处理器系统中,缓存行对齐可以减少缓存一致性协议带来的开销,主要原因如下:
    • 缓存一致性协议:在多处理器系统中,每个处理器都有自己的缓存,用于存储频繁访问的数据。为了确保所有处理器看到的数据是一致的,系统使用缓存一致性协议(如MESI协议)。当一个处理器修改了某个缓存行中的数据,其他处理器必须更新或失效其缓存中的相应数据。这种一致性维护会带来额外的开销。
    • 缓存行对齐的好处
    1. 减少伪共享:伪共享(False Sharing)是指多个处理器访问同一个缓存行中的不同数据项,导致频繁的缓存一致性操作。通过缓存行对齐,可以确保每个处理器访问的数据项位于不同的缓存行中,从而减少伪共享的发生。例如,如果两个处理器分别访问两个变量,而这两个变量恰好位于同一个缓存行中,那么每次一个处理器修改其中一个变量时,另一个处理器的缓存行也会失效,导致频繁的缓存一致性操作。通过对齐,可以将这两个变量放在不同的缓存行中,避免这种情况。
    2. 提高缓存命中率:缓存行对齐可以确保数据项在缓存中的位置是有序和连续的,从而提高缓存命中率。高缓存命中率意味着处理器可以更频繁地从缓存中读取数据,而不是从主内存中读取,从而提高系统性能。当数据项对齐到缓存行边界时,处理器可以更高效地预取和访问这些数据,减少缓存未命中的情况。
    3. 优化内存访问:缓存行对齐可以优化内存访问模式,使得内存访问更加高效。处理器可以更高效地加载和存储对齐的数据,从而减少内存访问的延迟。对齐的数据可以更好地利用处理器的缓存预取机制,减少内存访问的延迟。
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")))
//这个属性将变量放置在名为 .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

__ro_after_init 只读数据

1
2
3
4
5
6
7
8
/*
* __ro_after_init 用于标记 init 之后的只读内容(即
* 在调用 mark_rodata_ro() 之后)。这些实际上是只读的,
* 但可能会在 INIT 期间写入,因此不能存在于 .rodata 中(通过“const”)。
*/
#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) 尝试抢先获取指向的内存
通过地址 “x” 进入 CPU L1 高速缓存。
prefetch(x) 不应导致任何类型的异常,prefetch(0) 是
具体来说 OK。

prefetch() 应该由架构定义,如果不是,则使用
下面的 #define 提供了 no-op 定义。

有 2 个 prefetch() 宏:

prefetch(x) - 预取 “x” 处的 cacheline 进行读取
prefetchw(x) - 预取 “x” 处的 cacheline 进行写入

还有 PREFETCH_STRIDE 是 ArchiteCure 的首选
“lookahead” 大小来预取流式作。

*/

prefetch 预取 “x” 处的 cacheline 进行读取

1
2
3
#ifndef ARCH_HAS_PREFETCH
#define prefetch(x) __builtin_prefetch(x) //使用编译器的预取指令
#endif

arch/arm/include/asm/processor.h

  1. pld 是 ARM 汇编指令,用于预取数据到缓存中。它的作用是将指定地址的数据预取到 CPU 的 L1 缓存中,以提高后续访问的速度。
  2. pldw 是 ARM 汇编指令,用于预取数据到缓存中,并且它会将数据标记为“写入”。这意味着预取的数据可以在后续操作中被修改。
  3. __ALT_SMP_ASM 是一个宏,用于在单处理器和多处理器系统之间切换不同的汇编指令。它的作用是根据系统的配置选择适当的汇编指令。
  4. pld\t%a0 是 ARM 架构的预取指令,其中 pld 表示预取数据(Preload Data)。%a0 是一个占位符,表示将寄存器或内存地址替换为 ptr 的值。
  5. “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
/*
* Prefetching support - only ARMv5.
*/
#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
/*
* Architectures might want to move the poison pointer offset
* into some well-recognized area such as 0xdead000000000000,
* that is also not mappable by user-space exploits:
*/
#ifdef CONFIG_ILLEGAL_POINTER_VALUE
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
#else
# define POISON_POINTER_DELTA 0
#endif

/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x122 + POISON_POINTER_DELTA)

/********** include/linux/timer.h **********/
#define TIMER_ENTRY_STATIC ((void *) 0x300 + POISON_POINTER_DELTA)

/********** mm/page_poison.c **********/
#define PAGE_POISON 0xaa

/********** mm/page_alloc.c ************/

#define TAIL_MAPPING ((void *) 0x400 + POISON_POINTER_DELTA)

/********** mm/slab.c **********/
/*
* Magic nums for obj red zoning.
* Placed in the first word before and the first word after an obj.
*/
#define SLUB_RED_INACTIVE 0xbb /* when obj is inactive */
#define SLUB_RED_ACTIVE 0xcc /* when obj is active */

/* ...and for poisoning */
#define POISON_INUSE 0x5a /* for use-uninitialised poisoning */
#define POISON_FREE 0x6b /* for use-after-free poisoning */
#define POISON_END 0xa5 /* end-byte of poisoning */

/********** arch/$ARCH/mm/init.c **********/
#define POISON_FREE_INITMEM 0xcc

/********** fs/jbd/journal.c **********/
#define JBD_POISON_FREE 0x5b
#define JBD2_POISON_FREE 0x5c

/********** drivers/base/dmapool.c **********/
#define POOL_POISON_FREED 0xa7 /* !inuse */
#define POOL_POISON_ALLOCATED 0xa9 /* !initted */

/********** drivers/atm/ **********/
#define ATM_POISON_FREE 0x12
#define ATM_POISON 0xdeadbeef

/********** kernel/mutexes **********/
#define MUTEX_DEBUG_INIT 0x11
#define MUTEX_DEBUG_FREE 0x22
#define MUTEX_POISON_WW_CTX ((void *) 0x500 + POISON_POINTER_DELTA)

/********** security/ **********/
#define KEY_DESTROY 0xbd

/********** net/core/page_pool.c **********/
#define PP_SIGNATURE (0x40 + POISON_POINTER_DELTA)

/********** net/core/skbuff.c **********/
#define SKB_LIST_POISON_NEXT ((void *)(0x800 + POISON_POINTER_DELTA))
/********** net/ **********/
#define NET_PTR_POISON ((void *)(0x801 + POISON_POINTER_DELTA))

/********** kernel/bpf/ **********/
#define BPF_PTR_POISON ((void *)(0xeB9FUL + POISON_POINTER_DELTA))

/********** VFS **********/
#define VFS_PTR_POISON ((void *)(0xF5 + POISON_POINTER_DELTA))

/********** lib/stackdepot.c **********/
#define STACK_DEPOT_POISON ((void *)(0xD390 + POISON_POINTER_DELTA))

include/linux/uaccess.h: (内存标记) 提供内核空间与用户空间之间安全拷贝数据的核心函数,如 copy_to_user()

user_access_save user_access_restore

  • user_access_saveuser_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 /* CONFIG_DEBUG_VM */