=============================== 博客点滴积累,部分话语和知识点来源于网络,感谢网络资源的提供者======
1 先看tasklet 处理函数在哪里注册的
start_kernel(void)
softirq_init();
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
//softirq_vec[32] 数组存放不同类型的软中断回调函数
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
2 看看tasklet_action
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);// 我们使用tasklet注册的回调函数在这里执行
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
3 tasklet_action 什么是否被调用呢?
继续看看软中断线程创建的过程:
asmlinkage void __init start_kernel(void)
rest_init();
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init(void * unused);
do_pre_smp_initcalls();
spawn_ksoftirqd();
smp_init();
cpu_up(cpu);
err = _cpu_up(cpu, 0);
__raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE | mod, hcpu,
-1, &nr_calls);//SMP 每一个CPU创建一个softirq线程
static struct notifier_block __cpuinitdata cpu_nfb = {
.notifier_call = cpu_callback
};
__init int spawn_ksoftirqd(void)
{
void *cpu = (void *)(long)smp_processor_id();
int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);//CPU_UP_PREPARE 创建ksoftirqd 后台线程
BUG_ON(err == NOTIFY_BAD);
cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);//CPU_ONLINE 唤醒 ksoftirqd
register_cpu_notifier(&cpu_nfb); //注册内核通知链
return 0;
}
static int __cpuinit cpu_callback(struct notifier_block *nfb,unsigned long action,void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct task_struct *p;
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN://创建ksoftirqd 线程
p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
if (IS_ERR(p)) {
printk("ksoftirqd for %i failed\n", hotcpu);
return NOTIFY_BAD;
}kthread_bind(p, hotcpu);per_cpu(ksoftirqd, hotcpu) = p;break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN://唤醒ksoftirqd
wake_up_process(per_cpu(ksoftirqd, hotcpu));
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
if (!per_cpu(ksoftirqd, hotcpu))
break;
/* Unbind so it can run. Fall thru. */
kthread_bind(per_cpu(ksoftirqd, hotcpu),
any_online_cpu(cpu_online_map));
case CPU_DEAD:
case CPU_DEAD_FROZEN: {
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
p = per_cpu(ksoftirqd, hotcpu);
per_cpu(ksoftirqd, hotcpu) = NULL;
sched_setscheduler(p, SCHED_FIFO, ¶m);
kthread_stop(p);
takeover_tasklets(hotcpu);
break;
}
#endif /* CONFIG_HOTPLUG_CPU */
}
return NOTIFY_OK;
}
到下面这里softirq线程被创建,该线程里会调用task_action
cpu_callback()
ksoftirqd(void * __bind_cpu)// 被创建的softirq线程
do_softirq();
__do_softirq(void);
h = softirq_vec;
do {
if (pending & 1) {//是否被标记过,标记过就运行
h->action(h);//此时调用tasklet_action
rcu_bh_qsctr_inc(cpu);
}
h++;
pending >>= 1;
} while (pending);
4 如何使用呢?先看看我们需要关注的函数
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t; //加入链表
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
inline fastcall void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); //标记一下,将要运行的软中断函数
if (!in_interrupt())
wakeup_softirqd();//唤醒ksoftirqd
}
使用方法很简单:
1) 初始化调用 tasklet_init 初始化 struct tasklet_struct
2) 调用tasklet_schedule 函数将struct tasklet_struct 加入链接,唤醒softirq线程
注意:使用task_let 时,它不是确定时间的调度执行,不能阻塞,不能休眠