1、概述
local_irq_enable/local_irq_disable和local_irq_save/local_irq_restore两对函数在内核代码中出现的频率非常之高。每个函数的功能描述如下:
local_irq_enable/local_irq_disable:开关本地CPU总中断。
local_irq_save:保存当前CPU CPSR寄存器值,然后关闭本地CPU总中断
local_irq_restore:恢复之前的CPSR寄存器值。
思考1:local_irq_disable关闭中断之后触发的中断丢失了吗?
2、细节实现
下面我们将上面的两对函数的定义贴出来:
include/linux/irqflags.h
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
#define local_irq_save(flags) do {raw_local_irq_save(flags);} while (0)
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
/*
* Wrap the arch provided IRQ routines to provide appropriate checks.
*/
#define raw_local_irq_disable() arch_local_irq_disable()
#define raw_local_irq_enable() arch_local_irq_enable()
#define raw_local_irq_save(flags) \
do { \
typecheck(unsigned long, flags); \
flags = arch_local_irq_save(); \
} while (0)
#define raw_local_irq_restore(flags) \
do { \
typecheck(unsigned long, flags); \
arch_local_irq_restore(flags); \
} while (0)
CPSID是一个处理器状态切换指令,用于禁用中断或异常。它由两个部分组成:CPS(Change Processor State)和ID(Interrupt or abort disable)。
这个指令可以单独更改处理器状态寄存器(CPSR)中的 A、I、F 位,但不影响其他位。
CPS指令只能在特权模式下使用,即当程序处于C代码的默认用户模式时,不能直接操作CPS指令,需要使用汇编语言来操作。
此外,CPS指令不能被中断阻塞,且在中断或异常禁止模式下(ID=1),不能使用条件汇编指令。
CPSID的具体用法如下:
- CPSID I:禁用 IRQ 中断或 abort 异常。
- CPSID F:禁用 FIQ 中断或 abort 异常。
- CPSID I PRIMASK=1:关中断,同时设置 PRIMASK 位为 1。
- CPSID F FAULTMASK=1:关异常,同时设置 FAULTMASK 位为 1。
需要注意的是,CPSID指令在禁用中断或异常时,不会影响其他类型的异常处理,如 NMI、Hard Fault 等。此外,CPSID指令与CPSIE指令是对应的,CPSIE用于开启中断或异常。
arch/arm/include/asm/irqflags.h中关于硬中断内嵌汇编实现:
/*
* CPU interrupt mask handling.
*/
#ifdef CONFIG_CPU_V7M
#define IRQMASK_REG_NAME_R "primask"
#define IRQMASK_REG_NAME_W "primask"
#define IRQMASK_I_BIT 1
#else
#define IRQMASK_REG_NAME_R "cpsr"
/*cpsr_c代表的是这32位中的低8位,也就是控制位*/
#define IRQMASK_REG_NAME_W "cpsr_c"
#define IRQMASK_I_BIT PSR_I_BIT
#endif
#if __LINUX_ARM_ARCH__ >= 6
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags;
asm volatile(
"mrs %0, " IRQMASK_REG_NAME_R " @ arch_local_irq_save\n"
"cpsid i"
: "=r" (flags) : : "memory", "cc");
return flags;
}
/*CPSIE I:使能 IRQ 中断或 abort 异常*/
static inline void arch_local_irq_enable(void)
{
asm volatile(
"cpsie i @ arch_local_irq_enable"
:
:
: "memory", "cc");
}
/*CPSID I:禁用 IRQ 中断或 abort 异常*/
static inline void arch_local_irq_disable(void)
{
asm volatile(
"cpsid i @ arch_local_irq_disable"
:
:
: "memory", "cc");
}
#define local_fiq_enable() __asm__("cpsie f @ __stf" : : : "memory", "cc")
#define local_fiq_disable() __asm__("cpsid f @ __clf" : : : "memory", "cc")
/*
* restore saved IRQ & FIQ state
*/
static inline void arch_local_irq_restore(unsigned long flags)
{
asm volatile(
"msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore"
:
: "r" (flags)
: "memory", "cc");
}
local_irq_enable/local_irq_disable:开关本地CPU总中断。
local_irq_save:保存当前CPU CPSR寄存器值,然后关闭本地CPU总中断
local_irq_restore:恢复之前的CPSR寄存器值。