tasklet

kernel中用tasklet_struct来表示一个tasklet
struct tasklet_struct
{
    struct tasklet_struct *next;
    unsigned long state;
    atomic_t count;
    void (*func)(unsigned long);
    unsigned long data;
};
其中的func表示要执行的回调函数,而data则表示要传递给func的数据,
func形参是unsigned long,而data也是unsigned long,也正好证明了这一点.
tasklet的生命有两种方法
第一种
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
tasklet_struct 结构体有个state,表示tasklet的状态.
DECLARE_TASKLET 申明的tasklet默认是enable的,而DECLARE_TASKLET_DISABLED 声明的tasklet默认是disable
第二种
直接调用tasklet_init
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;
}
初始化完成后调用tasklet_schedule 来运行这个tasklet
static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}
tasklet的state 有两种状态
enum
{
    TASKLET_STATE_SCHED,    /* Tasklet is scheduled for execution */
    TASKLET_STATE_RUN    /* Tasklet is running (SMP only) */
};
tasklet_schedule 首先判断当前这个tasklet是否已经被sched了,也就是一个tasklet不能被sched两次
如果没有被sched,则调用__tasklet_schedule
void __tasklet_schedule(struct tasklet_struct *t)
{
    unsigned long flags;
    local_irq_save(flags);
    t->next = NULL;
    *__this_cpu_read(tasklet_vec.tail) = t;
    __this_cpu_write(tasklet_vec.tail, &(t->next));
    raise_softirq_irqoff(TASKLET_SOFTIRQ);
    local_irq_restore(flags);
}
而__tasklet_schedule ,首先调用local_irq_save/local_irq_restore保护临界区,然后将这个tasklet放到tasklet_vec这个list的最后
然后触发软件中断TASKLET_SOFTIRQ。
而tasklet_vec 是一个pcpu变量,也就是每一个cpu上上的tasklet组成一个list.
struct tasklet_head {
    struct tasklet_struct *head;
    struct tasklet_struct **tail;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);

到这一步使用tasklet的工作就完成了,剩下就等kernel来调用tasklet来运行客户初始化tasklet时定义的回调函数.
而这一的起点是raise_softirq_irqoff(TASKLET_SOFTIRQ);
这个函数触发了TASKLET_SOFTIRQ的软件中断,其回调函数是:
void __init softirq_init(void)
{
    int cpu;
    for_each_possible_cpu(cpu) {
        per_cpu(tasklet_vec, cpu).tail =
            &per_cpu(tasklet_vec, cpu).head;
        per_cpu(tasklet_hi_vec, cpu).tail =
            &per_cpu(tasklet_hi_vec, cpu).head;
    }
    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
在softirq_init 的时候注册TASKLET_SOFTIRQ的回调函数是tasklet_action。
static __latent_entropy void tasklet_action(struct softirq_action *a)
{
    struct tasklet_struct *list;
    local_irq_disable();
    list = __this_cpu_read(tasklet_vec.head);
    __this_cpu_write(tasklet_vec.head, NULL);
    __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
    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_unlock(t);
                continue;
            }
            tasklet_unlock(t);
        }
        local_irq_disable();
        t->next = NULL;
        *__this_cpu_read(tasklet_vec.tail) = t;
        __this_cpu_write(tasklet_vec.tail, &(t->next));
        __raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_enable();
    }
}
这个函数也是调用local_irq_disable/local_irq_enable 保护临界区,然后执行list中的每一项。
*__this_cpu_read(tasklet_vec.tail) = t;
之后t=tasklet_vec.head。因为在while循环之前__this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
最后更新__this_cpu_write(tasklet_vec.tail, &(t->next));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值