软件可以禁止中断,使处理器不响应所有中断请求,但是不可屏蔽中断(Non Maskable Interrupt,NMI)是个例外。
一、接口
1、禁止中断的接口
local_irq_disable()直接关闭当前 CPU 的中断功能,之后这个 CPU 不会再响应任何中断请求。local_irq_save(flags)先记住当前 CPU 的中断开关状态(保存到变量flags里),然后再关闭中断。这样后续可以通过其他函数恢复到保存前的状态。
关键点
- 只对当前运行的 CPU 有效,其他 CPU 的中断不会受影响
- 关闭后,当前 CPU 会完全忽略所有中断信号
ARM64 架构禁止中断的函数 local_irq_disable() 实现:
c
// 调用链:local_irq_disable() → raw_local_irq_disable() → arch_local_irq_disable()
// arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_disable(void)
{
asm volatile(
"msr daifset, #2 // arch_local_irq_disable"
:
:
: "memory");
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
把处理器状态的中断掩码位设置成 1,从此以后处理器不会响应中断请求。
2、开启中断
ARM64 架构中控制中断的函数:
- 直接方法:
local_irq_enable():立即打开当前处理器的中断功能。
- 保存和恢复中断状态的成对函数:
local_irq_restore(flags):用之前保存的flags值恢复当前处理器的中断状态。
关键使用规则
- 直接开关函数有局限:不能重复使用
local_irq_disable()关闭后再多次使用local_irq_enable()开启,这样会导致不可预测的结果。 - 安全的嵌套方法:使用
local_irq_save(flags)保存状态并获取标志后,可以多次嵌套调用该函数和对应的local_irq_restore(flags)恢复,系统能正确处理多次保存和恢复的层级关系。
简单总结
直接开关函数像简单的灯开关,按一下开/关,不能连续多次操作;而保存/恢复函数像带记忆功能的开关,能记住每次操作前的状态,支持复杂场景下的安全使用。
c
// 调用链:local_irq_enable() → raw_local_irq_enable() → arch_local_irq_enable()
// arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
asm volatile(
"msr daifclr, #2 // arch_local_irq_enable"
:
:
: "memory");
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
把处理器状态的中断掩码位设置成 0。
二、禁止/开启单个中断
在 ARM64 架构的 GIC 中断控制器中:
如果要开启中断 n:
- 调用
enable_irq(n)函数 - 这会设置 GICD_ISENABLERn 寄存器
- 让控制器允许该中断传递到 CPU
- 调用
如果要关闭中断 n:
- 调用
disable_irq(n)函数 - 这会设置 GICD_ICENABLERn 寄存器
- 让控制器屏蔽该中断
- 调用
简单来说
- 函数参数 n 就是你要控制的中断号
- 开启时:写入 ISENABLER 寄存器 → 允许中断传递
- 关闭时:写入 ICENABLER 寄存器 → 阻断中断传递
- 只有当开启状态存在时,设备的中断信号才会被 CPU 接收处理
三、内核使用案例
文件位置
drivers/input/touchscreen/atmel_mxt_ts.c
c
static int mxt_acquire_irq(struct mxt_data *data)
{
int error;
enable_irq(data->irq);
if (data->use_retrigen_workaround) {
error = mxt_process_messages_until_invalid(data);
if (error)
return error;
}
return 0;
}
static int mxt_soft_reset(struct mxt_data *data)
{
struct device *dev = &data->client->dev;
int ret = 0;
dev_info(dev, "Resetting device\n");
disable_irq(data->irq);
reinit_completion(&data->reset_completion);
ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
if (ret)
return ret;
/* Ignore CHG line for 100ms after reset */
msleep(MXT_RESET_INVALID_CHG);
mxt_acquire_irq(data);
ret = mxt_wait_for_completion(data, &data->reset_completion,
MXT_RESET_TIMEOUT);
if (ret)
return ret;
return 0;
}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
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