1. 软中断初始化
Linux目前支持10种sofirq,且不支持驱动开发者添加softirq(可以用tasklet)
10种类型如下:
enum
{
HI_SOFTIRQ=0,//最高优先级
TIMER_SOFTIRQ,//timer
NET_TX_SOFTIRQ,//网络收发数据
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,//块设备
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,//tasklet专有软中断
SCHED_SOFTIRQ,//调度软中断
HRTIMER_SOFTIRQ, //Hrtimer软中断/* Unused, but kept as tools rely on the
numbering. Sigh! */
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
系统初始化时,为每个cpu起一个RT进程处理softirq
smpboot_register_percpu_thread(&softirq_threads),处理函数为run_ksoftirqd
软中断按照优先级执行,软中断号越小,优先级越高。一个软中断,可以在不同cpu上运行,所以内核给每个cpu定义了软中断标记irq_stat,内核其他模块,可以通过open_softirq函数把软中断处理函数安装到softirq_vec数组。
2. 软中断的执行
内核模块可以调用raise_softirq(int nr)挂起一个软中断,
而系统在处理完硬件中断后调用irq_exit(),接着就会处理软中断
void irq_exit(void)
{
/*只有不在硬中断,软中断,或在软中断没有disable时,且本地cpu有软中断才会执行软中断 */
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();//处理软中断
}
__do_softirq重点代码分析
asmlinkage __visible void __do_softirq(void)
{
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);//进来先disable软中断,防止在irq_exit()进来两次
restart:
local_irq_enable();//打开中断,本地cpu可以响应硬件中断,也就是softriq在开中断状态执行
while ((softirq_bit = ffs(pending))) {//循环遍历pending,依次执行softirq
unsigned int vec_nr;
int prev_count;
h += softirq_bit - 1;
vec_nr = h - softirq_vec;
prev_count = preempt_count();
h->action(h);//执行softirq处理函数
h++;
pending >>= softirq_bit;
}
local_irq_disable();/* 关中断,再次判断本地cpu是否有softirq被挂起*/
pending = local_softirq_pending();
deferred = softirq_deferred_for_rt(pending);//如果本地cpu有RT进程,减少softirq执行
/*
这里有三个条件:
1. 一次__do_softirq执行不超过2ms,
2. 没有进行需要调度
3. restart执行不超过10次
*/
if (pending) {
if (time_before(jiffies, end) && !need_resched() &&
--max_restart)//irq_exit
goto restart;
}
/* 如果还有softirq需要执行,然后唤醒softirqd线程,继续处理软中断*/
if (pending | deferred)
wakeup_softirqd();
/*最后使能软中断 */
__local_bh_enable(SOFTIRQ_OFFSET);
}
3. tasklet实现
tasklet是特殊的sotfirq,处理函数如下
static void tasklet_action(struct softirq_action *a)
{
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {//这里保证一个tasklet,同一时间,只能在一个cpu运行
if (!atomic_read(&t->count)) {//判断tasklet是否被disable
if (!test_and_clear_bit(TASKLET_STATE_SCHED,
&t->state))
t->func(t->data);//执行tasklet处理函数
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
}
}
tasklet操作函数
tasklet_init
tasklet_kill/tasklet_kill_immediate
tasklet_schedule
tasklet_enable
tasklet_disable
4. softirq相关总结
1. 同一个softirq可以在不同的CPU上同时运行,softirq必须是可重入的
2. 软中断是在编译期间静态分配的,它不像tasklet那样能被动态的注册或去除
3. tasklet是基于softirq实现,同一个tasklet只能同时在一个cpu上执行,tasklet无需关心互斥问题.
4. 不同的tasklet可以在不同cpu上运行,提高了SMP效率
5. workqueue工作在线程环境,可以休眠.
6.softirq执行点有两个:irq_exit()和在进程调用raise_softirq()后,唤醒softirqd线程处理。