[Linux]学习笔记系列 -- 底层CPU与体系结构宏


title: 底层CPU与体系结构宏

categories:

  • linux
  • include
    tags:
  • linux
  • include
    abbrlink: febaf417
    date: 2025-10-03 09:01:49

https://github.com/wdfk-prog/linux-study

文章目录

  • [include/asm-generic/rwonce.h: 提供 READ_ONCE() 和 WRITE_ONCE() 宏,防止编译器优化,保证单次读写的原子性](#include/asm-generic/rwonce.h: 提供 READ_ONCE() 和 WRITE_ONCE() 宏,防止编译器优化,保证单次读写的原子性)
  • [include/asm-generic/irqflags.h 提供中断标志位的通用操作函数](#include/asm-generic/irqflags.h 提供中断标志位的通用操作函数)
    • [arch_irqs_disabled_flags 判断中断是否被禁用](#arch_irqs_disabled_flags 判断中断是否被禁用)
    • arch_irqs_disabled
  • [include/linux/irqflags.h 包含特定于体系结构的 irqflags.h,提供中断开关等操作](#include/linux/irqflags.h 包含特定于体系结构的 irqflags.h,提供中断开关等操作)
    • kernel/locking/irqflag-debug.c
    • [raw_check_bogus_irq_restore 检查中断恢复](#raw_check_bogus_irq_restore 检查中断恢复)
    • [local_irq_save 返回当前中断状态并禁用中断](#local_irq_save 返回当前中断状态并禁用中断)
    • [local_irq_restore 恢复中断状态](#local_irq_restore 恢复中断状态)
  • [include/linux/instruction_pointer.h: 提供获取当前指令指针(IP)的宏](#include/linux/instruction_pointer.h: 提供获取当前指令指针(IP)的宏)
    • [RET_IP 返回地址](#RET_IP 返回地址)
    • [THIS_IP 当前指令地址](#THIS_IP 当前指令地址)
  • [include/asm-generic/topology.h 提供描述CPU拓扑结构(如节点、核心)的通用定义](#include/asm-generic/topology.h 提供描述CPU拓扑结构(如节点、核心)的通用定义)
  • [include/linux/topology.h 包含特定于体系结构的CPU拓扑信息](#include/linux/topology.h 包含特定于体系结构的CPU拓扑信息)
  • [arch/arm/include/asm/word-at-a-time.h: ARM架构下用于一次性处理一个字(word)数据的优化函数](#arch/arm/include/asm/word-at-a-time.h: ARM架构下用于一次性处理一个字(word)数据的优化函数)
    • [WORD_AT_A_TIME_CONSTANTS 一次单词常量](#WORD_AT_A_TIME_CONSTANTS 一次单词常量)
    • [has_zero 检查一个无符号长整数 a 是否包含零字节,并返回一个掩码表示零字节的位置](#has_zero 检查一个无符号长整数 a 是否包含零字节,并返回一个掩码表示零字节的位置)
    • [prep_zero_mask zero_bytemask](#prep_zero_mask zero_bytemask)
    • create_zero_mask

include/asm-generic/rwonce.h: 提供 READ_ONCE() 和 WRITE_ONCE() 宏,防止编译器优化,保证单次读写的原子性

c 复制代码
/*
 * 阻止编译器合并或重新获取读取或写入。还禁止编译器对 READ_ONCE 和 WRITE_ONCE 的连续实例重新排序,但前提是编译器知道某些特定排序。使编译器了解 Sequences 的一种方法是将 READ_ONCE 或 WRITE_ONCE 的两次调用放在不同的 C 语句中。
 *
 * 这两个宏也适用于聚合数据类型,如结构体或联合体。
 *
 * 它们的两个主要用例是:(1) 调解进程级代码和 irq/NMI 处理程序之间的通信,所有处理程序都运行在同一个 CPU 上,以及 (2) 确保编译器不会折叠、纺锤或以其他方式破坏不需要排序或与提供所需排序的显式内存屏障或原子指令交互的访问。
 */

compiletime_assert_rwonce_type

  • __native_word 是一个宏,用于检查给定类型是否是本机字长(通常是 32 位或 64 位)。如果类型不是本机字长,则检查它的大小是否为 sizeof(long long),即 64 位。这个宏通常用于确保在进行原子操作时,数据类型的大小与系统架构相匹配。
c 复制代码
/* 是的,这允许在 32 位架构上进行 64 位访问。在某些情况下,这些实际上是原子的(即 Armv7 + LPAE),但对于其他情况,我们依赖于将访问分成 2x32 位访问,以获得 32 位数量(例如虚拟地址)和强大的盛行风。
 */
#define compiletime_assert_rwonce_type(t)					\
	compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),	\
		"Unsupported access size for {READ,WRITE}_ONCE().")

READ_ONCE 保证原子性读取

  • READ_ONCE 是一个宏,用于在多线程环境中安全地读取变量的值。它确保读取操作是原子的,即不会被其他线程的写入操作打断。这对于避免数据竞争和确保数据一致性非常重要。
  • compiletime_assert_rwonce_type 是一个编译时断言,用于检查传入的变量类型是否符合要求。它确保变量是原子类型或具有与长整型相同的大小。这有助于在编译时捕获潜在的错误,确保代码的正确性和安全性。
  • __READ_ONCE 是一个底层实现,用于执行实际的读取操作。它使用了 __unqual_scalar_typeof 来获取变量的类型,并将其转换为 const volatile 指针,以确保读取操作是原子的。
c 复制代码
/* 
 * 如果不需要任何原子性,请使用 __READ_ONCE() 而不是 READ_ONCE()。请注意,这可能会导致编译错误!
 */
#ifndef __READ_ONCE
#define __READ_ONCE(x)	(*(const volatile __unqual_scalar_typeof(x) *)&(x))
#endif

#define READ_ONCE(x)							\
({									\
	compiletime_assert_rwonce_type(x);				\
	__READ_ONCE(x);							\
})

include/asm-generic/irqflags.h 提供中断标志位的通用操作函数

arch_irqs_disabled_flags 判断中断是否被禁用

c 复制代码
/* test flags */
#ifndef arch_irqs_disabled_flags
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
	return flags == ARCH_IRQ_DISABLED;
}
#endif

arch_irqs_disabled

c 复制代码
/* test hardware interrupt enable bit */
#ifndef arch_irqs_disabled
static inline int arch_irqs_disabled(void)
{
	//arch_local_save_flags 返回当前的中断状态
	return arch_irqs_disabled_flags(arch_local_save_flags());
}
#endif

include/linux/irqflags.h 包含特定于体系结构的 irqflags.h,提供中断开关等操作

kernel/locking/irqflag-debug.c

c 复制代码
noinstr //禁止内联插桩的段
void warn_bogus_irq_restore(void)
{
	instrumentation_begin();
	WARN_ONCE(1, "raw_local_irq_restore() called with IRQs enabled\n");
	instrumentation_end();
}
EXPORT_SYMBOL(warn_bogus_irq_restore);

raw_check_bogus_irq_restore 检查中断恢复

c 复制代码
#ifdef CONFIG_DEBUG_IRQFLAGS
extern void warn_bogus_irq_restore(void);
#define raw_check_bogus_irq_restore()			\
	do {						\
		if (unlikely(!arch_irqs_disabled()))	\
			warn_bogus_irq_restore();	\
	} while (0)
#else
#define raw_check_bogus_irq_restore() do { } while (0)
#endif

local_irq_save 返回当前中断状态并禁用中断

  1. 返回当前的中断状态,并将其保存到 flags 变量中。
  2. 执行中断禁止操作

local_irq_restore 恢复中断状态

  • 恢复之前保存的中断状态,允许中断再次发生。

include/linux/instruction_pointer.h: 提供获取当前指令指针(IP)的宏

RET_IP 返回地址

c 复制代码
/*
 * _RET_IP_ 是一个宏,用于获取当前函数的返回地址。
 * 它通常用于调试和错误处理,以便在发生异常或错误时
 * 记录函数的返回地址。
 */
#define _RET_IP_		(unsigned long)__builtin_return_address(0)

THIS_IP 当前指令地址

  • _THIS_IP_ 是一个宏,用于获取当前指令的地址。它通常用于调试和错误处理,以便在发生异常或错误时记录当前指令的地址。
  • 具体来说,THIS_IP 被定义为一个复合语句表达式 ({ __label__ __here; __here: (unsigned long)&&__here; })。复合语句表达式是 GCC 的一种扩展语法,允许在表达式中包含多个语句。
  • 在这个复合语句表达式中,首先定义了一个局部标签 __here,这是通过 __label__ 关键字实现的。局部标签是一种特殊的标签,只在当前复合语句表达式的作用域内有效。
  • 接下来,表达式使用了标签 __here,并通过 &&__here 获取该标签的地址。标签地址运算符 &&GCC 的一个扩展,用于获取标签的地址。然后,将这个地址强制转换为 unsigned long 类型。
  • 最终,这个宏 _THIS_IP_ 提供了一种方法来获取当前代码位置的地址,并将其转换为 unsigned long 类型。这在调试和分析代码执行路径时可能非常有用,因为它允许程序员获取当前执行点的地址。
c 复制代码
#ifndef _THIS_IP_
#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })
#endif

include/asm-generic/topology.h 提供描述CPU拓扑结构(如节点、核心)的通用定义

cpu_to_node

c 复制代码
#ifndef cpu_to_node
// 逗号运算符的作用 在 C 语言中,逗号运算符(,)会依次计算其左侧和右侧的表达式,并返回右侧表达式的值。
#define cpu_to_node(cpu)	((void)(cpu),0)
#endif

include/linux/topology.h 包含特定于体系结构的CPU拓扑信息

numa_node_id

c 复制代码
static inline int numa_node_id(void)
{
	return cpu_to_node(raw_smp_processor_id());
}

arch/arm/include/asm/word-at-a-time.h: ARM架构下用于一次性处理一个字(word)数据的优化函数

WORD_AT_A_TIME_CONSTANTS 一次单词常量

c 复制代码
#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }

has_zero 检查一个无符号长整数 a 是否包含零字节,并返回一个掩码表示零字节的位置

c 复制代码
static inline unsigned long has_zero(unsigned long a, unsigned long *bits,
				     const struct word_at_a_time *c)
{
	/* 	one_bits表示每个字节的最低位为 1 
		high_bits表示每个字节的最高位为 1*/
	/* 如果某个字节为零,则 (a - c->one_bits) 的该字节会产生一个进位 */
	/* ~a 的该字节会为全 1 */
	/* 结合 c->high_bits,最终生成一个掩码,标记零字节的位置 */
	unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits;
	*bits = mask;
	return mask;
}

prep_zero_mask zero_bytemask

c 复制代码
#define prep_zero_mask(a, bits, c) (bits)

#define zero_bytemask(mask) (mask)

create_zero_mask

c 复制代码
static inline unsigned long create_zero_mask(unsigned long bits)
{
	bits = (bits - 1) & ~bits;
	return bits >> 7;
}
相关推荐
逑之9 小时前
C语言笔记15:动态内存管理
c语言·网络·笔记
行走的bug...9 小时前
cmake总结
linux·运维·服务器
zfxwasaboy9 小时前
DRM KMS 子系统(3)CRTC
linux·c语言
GISer_Jing9 小时前
AI Agent:学习与适应、模型上下文协议
人工智能·学习·设计模式·aigc
im_AMBER9 小时前
Leetcode 100 在链表中插入最大公约数
数据结构·c++·笔记·学习·算法·leetcode·链表
今儿敲了吗9 小时前
计算机网络第三章笔记(二)
笔记·计算机网络
凌波粒9 小时前
Linux 面试题篇
linux·运维·服务器
Joren的学习记录9 小时前
【Linux运维疑难杂症】k8s集群创建calico网络失败
linux·运维·kubernetes
时间黑客9 小时前
SolidWorks百日建模(二)
学习