关于 Linux 的中断:
进入中断服务程序时,硬件会自动关闭当前 CPU 的可屏蔽中断 IRQ,但不会影响其他 CPU 的中断处理。FIQ 中断不受影响,但是 Linux 内核不使用 FIQ。虽然可以在 ISR 中手动调用 local_irq_enable() 重新开启本地中断,但内核出于防止重入、栈溢出和竞态的考虑,不鼓励中断嵌套。
Linux 中断号是内核维护的虚拟中断号,与设备树中描述的硬件物理中断号不同,二者通过 irq_domain机制映射。
GIC 支持中断优先级设置,但标准 Linux 内核(非实时版本)不会让高优先级中断抢占正在执行的低优先级 ISR。中断处理是按顺序完成的,即使优先级更高,也必须等待当前 ISR 结束(除非显式启用中断嵌套或应用 RT-PREEMPT 等实时补丁)。
同一中断号的中断不会在同一 CPU 上嵌套执行,即使手动开启了全局中断。这是内核通过中断屏蔽和引用计数机制强制保证的,用于防止重入和系统不稳定。
i.MX6ULL 基于 ARM Cortex-A7,CPU 层面不支持 NMI 异常,SoC 也未提供专用的 NMI 引脚或中断源。
linux_kernel_wiki/文章/你真的理解Linux中断机制嘛.md at main · 0voice/linux_kernel_wiki · GitHub
local_irq_disable();
只管 IRQ,不会关 FIQ
上半部用来快速处理中断,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作。也就是我们常说的硬中断,特点是快速执行。
下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行。也就是我们常说的软中断,特点是延迟执行。
(3)proc 文件系统:是一种内核空间和用户空间进行通信的机制,可以用来查看内核的数据结构,或者用来动态修改内核的配置。
/proc/softirqs 提供了软中断的运行情况;
/proc/interrupts 提供了硬中断的运行情况。
(4)硬中断:硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上。硬中断可以直接中断CPU,引起内核中相关的代码被触发。
(5)软中断:软中断仅与内核相关,由当前正在运行的进程所产生。 通常,软中断是一些对I/O的请求,这些请求会调用内核中可以调度I/O发生的程序。 软中断并不会直接中断CPU,也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。 除了iowait(等待I/O的CPU使用率)升高,软中断(softirq)CPU使用率升高也是最常见的一种性能问题。
上半部:中断处理函数,完成一些紧急且不耗时的操作。
下半部:中断函数中没完成的一些耗时操作。
比如网卡,上半部:从网卡的缓存复制到内存中,下半部:对数据包进行校验和拆包等处理。
硬件中断
查看硬中断运行情况
查看软中断运行情况
线程中断
// 必须与 IRQF_ONESHOT 一起使用中断在设备树中的表示
中断相关函数
(1)中断申请/释放:
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev);
int devm_request_irq(struct device *dev,
unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev);
void free_irq(unsigned int irq, void *dev_id);参数:
irq:要申请的中断号,这个不是硬件手册上查到的号,而是内核中的号
flags:中断的触发方式或处理方式
IRQF_SHARED:共享中断
IRQF_ONESHOT:
IRQF_TRIGGER_RISING:上升沿触发
IRQF_TRIGGER_FALLING:下降沿触发
IRQF_TRIGGER_HIGH:高电平触发
IRQF_TRIGGER_LOW:低电平触发
name:中断名,可在 /proc/interrupts 查看
dev:设备 ID,当使用共享中断时使用设备 ID 来区分是哪个设备。
返回值:
0:成功
负数:失败
还有个线程中断不知道啥意思
(2)中断控制:
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void synchronize_irq(unsigned int irq);
设置触发类型:
int irq_set_irq_type(unsigned int irq,
unsigned int flow_type);
中断释放
void free_irq(unsigned int irq, void *dev_id);
/*
功能:释放中断号
参数:
irq:设备号
dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
*/gpio 到中断
int gpiod_to_irq(const struct gpio_desc *desc);中断处理函数:
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev_id);
static irqreturn_t my_irq(int irq, void *data)
{
struct mydev *d = data;
/* 处理中断 */
return IRQ_HANDLED;
}参数:
irq:中断号
dev_id:
返回值:
IRQ_HANDLED:处理完成
IRQ_NONE:共享中断
IRQ_WAKE_THREAD:
gpio 中断
desc = devm_gpiod_get(dev, "irq", GPIOD_IN);
irq = gpiod_to_irq(desc);
devm_request_threaded_irq(dev,
irq,
NULL,
my_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"my_irq",
data);
下半部机制
下半部在执行的过程中,。中断被重新使能,所以如果有新的硬件中断产生,将会停止执行下半部,转为执行上半部。
硬件中断发生
↓
CPU 自动关中断(硬件行为)
↓
Linux 上半部(hardirq)执行 ← 执行期间中断保持禁用
↓
★ 上半部结束前,Linux 会开中断 ← 关键点!
↓
如果有下半部(softirq/tasklet/workqueue)→ 异步执行
↓
返回用户空间时完全恢复中断状态软中断
1.软中断一般是“可延迟函数”的总称,它不能睡眠,不能阻塞,它处于中断上下文,不能进城切 换,软中断不能被自己打断,只能被硬件中断打断(上半部),可以并发的运行在多个 CPU 上。 所以软中断必须设计成可重入的函数,因此也需要自旋锁来保护其数据结构。
2.工作队列中的函数处在进程上下文中,它可以睡眠,也能被阻塞,能够在不同的进程间切 换。已完成不同的工作。 可延迟函数和工作队列都不能访问用户的进程空间,可延时函数在执行时不可能有任何正在 运行的进程,工作队列的函数有内核进程执行,他不能访问用户空间地址
tasklet
workqueue
工作队列。