1 上/下半部
将中断处理程序分为两部分:
1)上半部:做严格时限的工作
2)下半部:允许稍后执行的工作
2 使用
注册
/*
* irg - 表示要分配的中断号
* handler - 实际的中断处理程序
* flags - 标志位,表示此中断的具有特性
* name - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用
* dev - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序
* 返回值:
* 执行成功:0
* 执行失败:非0
*/
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char* name,
void *dev)
释放
void free_irq(unsigned int irq, void *dev)
3 上半部流程
4 下半部
下半部的3种机制:软中断、tasklet和工作队列。
4.1 软中断
在编译期间静态分配,不能动态注册或注销
软中断由softirq_action表示。
struct softirq_action
{
void (*action)(struct softirq_action *);
};
在kernel/softirq.c中,维护了softirq_action的数组
static struct softirq_action softirq_vec[NR_SOFTIRQS];
在include/linux/interrupt.h中当前内核所使用的软中断
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
注册软中断,设置action成员的处理函数
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
触发软中断
void raise_softirq(unsigned int nr)
之后将在合适的时间执行,在下列地方检查和执行:
1)从一个硬中断返回时
2)在ksoftirqd内核线程中被执行
3)在那些显式检查或执行待处理软中断的代码中,如网络子系统中
最终都要在do_softirq()中执行
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
if (in_interrupt())//在中断上下文中,或者是在禁止了软中断后调用了
return;
local_irq_save(flags);
pending = local_softirq_pending();//待处理软中断的位图
if (pending)
__do_softirq();
local_irq_restore(flags);
}
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
...
pending = local_softirq_pending();
...
h = softirq_vec;
do {
if (pending & 1) {//对应标志位被设置了
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(h - softirq_vec);
trace_softirq_entry(h, softirq_vec);
h->action(h); //执行action
trace_softirq_exit(h, softirq_vec);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count, preempt_count());
preempt_count() = prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while (pending);
...
}
4.2 tasklet
通过软中断实现,两类:HI_SOFTIRQ和TASKLET_SOFTIRQ
初始化时,start_kernel()–>softirq_init
void __init softirq_init(void)
{
...
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
tasklet由tasklet_struct结构体表示
struct tasklet_struct
{
struct tasklet_struct *next; /* 链表中的下一个tasklet */
unsigned long state; /* tasklet状态 */
atomic_t count; /* 引用计数器 */
void (*func)(unsigned long); /* tasklet处理函数 */
unsigned long data; /* tasklet处理函数的参数 */
};
已调度的tasklet存放在单处理器数据结构:tasklet_vec和tasklet_hi_vec
调度:tasklet_schedule()和tasklet_hi_schedule()
tasklet_schedule()–>__tasklet_schedule()–>raise_softirq_irqoff(TASKLET_SOFTIRQ),唤起对应的软中断,之后在do_softirq()中调用对应的action,即tasklet_action
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable(); //禁止中断
list = __get_cpu_var(tasklet_vec).head;
__get_cpu_var(tasklet_vec).head = NULL;//清空,防止SMP时影响
__get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
local_irq_enable();
while (list) { //遍历
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) { //检查并设置TASKLET_STATE_RUN
if (!atomic_read(&t->count)) {//检查是否被禁止
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);//执行对应的func
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;
__get_cpu_var(tasklet_vec).tail = &(t->next);
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
4.3 工作队列
把工作交由内核线程取执行,允许重新调度、睡眠。
相关数据结构
struct work_struct {
atomic_long_t data; /* 这个并不是处理函数的参数,而是表示此work是否pending等状态的flag */
#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
struct list_head entry; /* 中断下半部处理函数的链表 */
work_func_t func; /* 处理中断下半部工作的函数 */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
struct cpu_workqueue_struct {
spinlock_t lock; /* 锁保护这种结构 */
struct list_head worklist; /* 工作队列头节点 */
wait_queue_head_t more_work;
struct work_struct *current_work;
struct workqueue_struct *wq; /* 关联工作队列结构 */
struct task_struct *thread; /* 关联线程 */
} ____cacheline_aligned;
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq; /* 工作者线程 */
struct list_head list;
const char *name;
int singlethread;
int freezeable; /* Freeze threads during suspend */
int rt;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
workqueue_struct:工作者线程workqueue_struct用表示
cpu_workqueue_struct:每个处理器对应的工作者线程表示
work_struct:工作处理,组成链表
关系
4.4 总结
软中断 | tasklet | 工作队列 | |
---|---|---|---|
上下文 | 中断 | 中断 | 进程 |
顺序执行保障 | 无 | 同类型不能同时执行 | 无 |
选择时考虑:
1)是否需要休眠
2)性能