首页 > 人文 > >
Linux内核中的软中断、tasklet和工作队列详解( 四 )
2020-12-11
struct tasklet_struct{struct tasklet_struct *next;//将多个tasklet链接成单向循环链表unsigned long state;//TASKLET_STATE_SCHED(Tasklet is scheduled for execution)TASKLET_STATE_RUN(Tasklet is running (SMP only))atomic_t count;//0:激活tasklet 非0:禁用taskletvoid (*func)(unsigned long); //用户自定义函数unsigned long data;//函数入参};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);//低优先级static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);//高优先级
相关API
#define DECLARE_TASKLET(name, func, data) \struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }//定义名字为name的非激活tasklet#define DECLARE_TASKLET_DISABLED(name, func, data) \struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } //定义名字为name的激活taskletvoid tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)//动态初始化tasklet
static inline void tasklet_disable(struct tasklet_struct *t)//函数暂时禁止给定的tasklet被tasklet_schedule调度 , 直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出static inline void tasklet_enable(struct tasklet_struct *t)//使能一个之前被disable的tasklet;若这个tasklet已经被调度, 它会很快运行 。 tasklet_enable和tasklet_disable必须匹配调用, 因为内核跟踪每个tasklet的"禁止次数"static inline void tasklet_schedule(struct tasklet_struct *t)//调度 tasklet 执行 , 如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己tasklet_hi_schedule(struct tasklet_struct *t)//和tasklet_schedule类似 , 只是在更高优先级执行 。 当软中断处理运行时, 它处理高优先级 tasklet 在其他软中断之前 , 只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期.tasklet_kill(struct tasklet_struct *t)//确保了 tasklet 不会被再次调度来运行 , 通常当一个设备正被关闭或者模块卸载时被调用 。 如果 tasklet 正在运行, 这个函数等待直到它执行完毕 。 若 tasklet 重新调度它自己 , 则必须阻止在调用 tasklet_kill 前它重新调度它自己 , 如同使用 del_timer_sync
实现原理
static inline void tasklet_schedule(struct tasklet_struct *t){if (!test_and_set_bit(TASKLET_STATE_SCHED, }void __tasklet_schedule(struct tasklet_struct *t){unsigned long flags;local_irq_save(flags);t->next = NULL;*__get_cpu_var(tasklet_vec).tail = t;__get_cpu_var(tasklet_vec).tail = //加入低优先级列表raise_softirq_irqoff(TASKLET_SOFTIRQ);//触发软中断local_irq_restore(flags);}
tasklet执行过程 TASKLET_SOFTIRQ对应执行函数为tasklet_action , HI_SOFTIRQ为tasklet_hi_action , 以tasklet_action为例说明 , tasklet_hi_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;__get_cpu_var(tasklet_vec).tail = //取得tasklet链表local_irq_enable();while (list) {struct tasklet_struct *t = list;list = list->next;if (tasklet_trylock(t)) {if (!atomic_read(t->func(t->data);tasklet_unlock(t);continue;}tasklet_unlock(t);}//如果t->count的值不等于0 , 说明这个tasklet在调度之后 , 被disable掉了 , 所以会将tasklet结构体重新放回到tasklet_vec链表 , 并重新调度TASKLET_SOFTIRQ软中断 , 在之后enable这个tasklet之后重新再执行它local_irq_disable();t->next = NULL;*__get_cpu_var(tasklet_vec).tail = t;__get_cpu_var(tasklet_vec).tail =__raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_enable();}}