07、中断屏蔽
一、什么是中断屏蔽
在单核CPU中,防止程序同时访问共享数据出错的简单方法是:执行关键代码时先关掉中断。这样就能避免程序中途被其他任务打断,从而杜绝数据冲突问题。因为Linux系统是通过中断来切换任务的,关中断后系统暂时无法切换任务,CPU就会一口气把当前程序的关键操作执行完,其他程序在这期间不会被调度进来。简单来说就是:关中断→锁住当前进程→安全执行关键操作→再开中断恢复调度。
二、API
2.1、中断控制函数(IRQ)
功能定位:用于控制CPU对硬件中断的响应,确保临界区代码执行的原子性。
核心函数及作用:
c
void local_irq_disable(void); // 关闭当前CPU的硬件中断
// 作用:禁止所有外部硬件触发的中断请求
// 应用场景:进入需要独占CPU的临界区前调用
1
2
3
2
3
c
void local_irq_enable(void); // 重新开启当前CPU的硬件中断
// 作用:恢复中断响应能力
// 注意:仅在已关闭中断的状态下调用才有意义
1
2
3
2
3
c
unsigned long flags;
void local_irq_save(unsigned long *flags); // 保存并关闭中断
// 作用:原子性地记录当前中断状态(保存到flags)并关闭中断
// 核心机制:通过CPU寄存器操作实现状态保存
1
2
3
4
2
3
4
c
void local_irq_restore(unsigned long flags); // 根据flags恢复中断状态
// 作用:根据保存的flags值决定是否重新开启中断
// 设计目的:避免因嵌套调用导致的意外状态
1
2
3
2
3
技术细节:
- 这些函数通过操作CPU的
interrupt flag
(如x86的IF标志位)实现 local_irq_save
/restore
组合常用于需要保留原中断状态的场景,例如:
c
unsigned long flags;
local_irq_save(&flags);
// 临界区代码
local_irq_restore(flags); // 恢复调用前的中断状态
1
2
3
4
2
3
4
2.2、底半部控制函数(BH)
功能定位:控制软件触发的软中断(Softirq)和tasklet处理,确保临界区不被内核调度的软件任务打断。
核心函数及作用:
c
void local_bh_disable(void); // 禁止当前CPU的软中断和tasklet
// 作用:阻止底半部任务的调度执行
// 关键机制:通过设置`in_softirq`标志位实现
1
2
3
2
3
c
void local_bh_enable(void); // 恢复软中断和tasklet的处理
// 作用:允许底半部任务重新调度
// 注意:需与`local_bh_disable`成对使用
1
2
3
2
3
技术细节:
底半部(Bottom Halves)是Linux内核的软件中断机制,包含:
软中断(Softirq):如网络包接收处理(NET_RX)、任务调度(TASKLET)
tasklet:基于软中断实现的轻量级任务单元
local_bh_disable
会阻断所有底半部任务的执行,但不会影响硬件中断
2.3、技术对比与协作机制
特性 | 中断控制(IRQ) | 底半部控制(BH) |
---|---|---|
触发源 | 硬件事件(如定时器、IO) | 软件事件(如内核调度) |
执行环境 | 中断上下文(不可睡眠) | 进程上下文(可睡眠) |
屏蔽范围 | 禁用所有外部硬件中断 | 禁用软中断和tasklet |
典型使用场景 | 保护共享硬件寄存器 | 防止递归调用或资源竞争 |
协作示例:
c
// 驱动程序典型模式
local_irq_disable(); // 禁用硬件中断
local_bh_disable(); // 禁用软件任务
// 关键资源操作
local_bh_enable(); // 恢复软件任务
local_irq_enable(); // 恢复硬件中断
1
2
3
4
5
6
2
3
4
5
6
三、案例
案例代码目录:drivers/gpu/drm/i2c/tda998x_drv.c
执行以下操作:
- 禁用本地中断(local_irg_disable)
- 执行GPIO控制: a. 将GPIO引脚电平拉高 b. 保持高电平状态10毫秒 c. 将GPIO引脚电平拉低
- 恢复本地中断功能(local_irg_enable)
这个流程确保在安全的中断保护环境下完成精确的GPIO电平控制,先禁用中断防止干扰,完成指定操作后再重新启用中断功能。