Linux 软中断后台线程的创建过程及tasklet的使用

===============================   博客点滴积累,部分话语和知识点来源于网络,感谢网络资源的提供者======


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, &param);

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 时,它不是确定时间的调度执行,不能阻塞,不能休眠

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值