GIC 架构
在分析软中断之前,先分析GIC架构,通过GIC 来了解中断处理的宏观逻辑,之后介绍软中断引入原因以及具体的使用方式。

GIC分成两大部分: distributor 和 CPU interface, distributor 连接外设中断源,而CPU interface 连接CPU。
Distributor
Distributor 根据 irq 优先级分发 irq 到 CPU,它有如下特征
- 整体控制分发interrupt 到 CPU
- 单独 enable/disable interrupt
- 设置 interrupt 优先级
- 设置 irq 的处理 CPU
- 设置 interrupt 的触发方式: 边缘触发 或者电平触发
- 设置 interrupt 的 group: group0 或者 group1.
CPU interfaces
- enable 处理器的中断请求信号
- 发送 ack 信号
- 提示中断处理结束
- 设置中断优先级
- 设置处理器的处理竞争策略
- 设置最高优先级的 pending interrupt
不同于软件上的理解,我们通常说的中断触发,是先经由中断源通过 distributor 送给GIC,之后经由 GIC 的CPU interface 按照优先级送到CPU 进行处理。而CPU在处理之后需要发送ACK。
为什么需要软中断
要解释为什么需要软中断,在 中断架构 中,我们提到中断上下文的概念,在早期的实现中,支持中断嵌套,这带来了不确定性,因此通常在irq 处理上下文中,会关闭中断响应,如果 irq handler 处理时间过长,就会导致进程得不到调度,从而引起一系列问题。为了解决这个问题,linux 提出了中断 top half 和 bottom half 的概念。
在 top half 中,系统只作对中断的响应,剩下的逻辑会在 irq enable 之后放在 bottom half 中处理。

中断底半部的实现
中断底半部有三种实现方式: softirq, tasklet, workqueue. 其目的都是为了解决 irq disable 问题。对于 softirq 通常是固定好的,比如 timer, 一般作驱动开发不会使用到,所以这里主要列举tasklet 和 workqueue 使用方式。
tasklet 典型使用
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
void do_something(unsigned long args)
{
pr_err("%s\n", __func__);
schedule();
}
static struct tasklet_struct task;
static int test_bh_init(void)
{
printk(KERN_ERR "%s\n", __func__);
tasklet_init(&task, do_something, 0);
tasklet_schedule(&task);
return 0;
}
static void test_bh_exit(void)
{
printk(KERN_ERR "%s\n", __func__);
tasklet_kill(&task);
}
module_init(test_bh_init);
module_exit(test_bh_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
workqueue 典型使用
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kfifo.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
typedef struct debug_data {
int irq;
struct work_struct worker;
} debug_data_t;
void do_somework(struct work_struct *work)
{
debug_data_t *data = container_of(work, debug_data_t, worker);
pr_err("irq:%d\n", data->irq);
}
static debug_data_t *global_data = NULL;
static int debug_workqueue_init(void)
{
global_data = (debug_data_t *)kmalloc(sizeof(debug_data_t), GFP_KERNEL);
if (!global_data) {
pr_err("no memory");
return -ENOMEM;
}
global_data->irq = 102;
INIT_WORK(&global_data->worker, do_somework);
schedule_work(&global_data->worker);
return 0;
}
static void debug_workqueue_exit(void)
{
if (global_data)
kfree(global_data);
}
module_init(debug_workqueue_init);
module_exit(debug_workqueue_exit);
MODULE_LICENSE("GPL");
MODULE_LICENSE("xxx");
小问题
如果在 irq handler、 softirq handler 中调用 schedule, 会出现异常吗?可以尝试一下。结果可能和常识上有些偏差。
本文介绍了GIC(Generic Interrupt Controller)架构,包括distributor和CPU interface的功能,阐述了中断处理的流程。软中断的引入是为了解决中断处理中的不确定性和长时间阻塞问题,通过中断tophalf和bottomhalf的概念来优化处理。文章还详细讲解了tasklet和workqueue两种常见的中断底半部实现方式,并提供了示例代码。最后提出了一个问题:在irqhandler或softirqhandler中调用schedule可能产生的异常情况。
1369

被折叠的 条评论
为什么被折叠?



