7.5 Tasklet
关于时间,内核另外一个有力工具就是tasklet机制。它主要用于中断管理。
Tasklet
在某些方面与内核定时器类似。它们总是在中断时运行,总是运行在调度它们的CPU上,且能够接受unsigned long
类型的参数。又不像内核定时器,不能要求在特定时间执行该功能。通过调度tasklet
,你可以让内核选择在稍后的时间执行它。这种行为对于中断处理程序非常有用,硬件中断必须尽可能快的处理,但是数据处理可以延迟后安全地进行。事实上,tasklet
,就像内核定时器一样,在“软中断”的上下文中被执行(原子模式),这是内核在使能硬件中断的情况下执行异步任务的一种机制。
tasklet
的数据结构如下所示,在使用之前必须被初始化。声明可以使用下面的函数或宏。
#include <linux/interrupt.h>
struct tasklet_struct {
/* ... */
void (*func)(unsigned long);
unsigned long data;
};
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
Tasklet
提供了许多有趣的功能:
tasklet
可以禁用和使能;- 就像计时器一样,
tasklet
可以重新注册自己; tasklet
可以以正常优先级或高优先级调度运行。后来的组总是先执行。- 如果系统负荷不是很重,
tasklet
可以立即执行,但是不迟于下一个定时器嘀嗒; tasklet
可以并发执行,但是同一个tasklet
只能在一个处理器上运行且运行在调度它的CPU之上。
jit
模块包含2个文件,/proc/jitasklet
和/proc/jitasklethi
,其返回与/proc/jitimer
相同的数据。数据内容如下:
phon% cat /proc/jitasklet
time delta inirq pid cpu command
6076139 0 0 4370 0 cat
6076140 1 1 4368 0 cc1
6076141 1 1 4368 0 cc1
6076141 0 1 2 0 ksoftirqd/0
6076141 0 1 2 0 ksoftirqd/0
6076141 0 1 2 0 ksoftirqd/0
第1行数据描述了调用进程的内容,其它行描述了运行tasklet
程序的内容。
上面的数据证实,当CPU正在运行一个进程的时候,tasklet
会在下一个定时器嘀嗒运行;否则,当CPU处于空闲时,它会立即运行。内核提供了一组ksoftirqd
内核线程,每个CPU一个,只是为了运行“软中断”处理程序,例如tasklet_action
函数。因此,上面最后三个tasklet
发生在ksoftirqd
内核线程里,其运行在CPU 0上。jitasklethi
的实现,使用了高优先级的tasklet
。下面的函数列表会有介绍。
在jit
模块里, /proc/jitasklet
和/proc/jitasklethi
的实现与/proc/jitimer
几乎一样。前两个使用的是tasklet
调用,后一个使用的timer
相关调用。下面的列表就是tasklet
相关接口:
void tasklet_disable(struct tasklet_struct *t);
禁止给定的
tasklet
。仍可以使用tasklet_schedule
调度tasklet
,但是会延迟执行,直到再次使能tasklet
为止。如果tasklet当前正在运行,则此函数将等待,直到tasklet
退出; 因此,在调用tasklet_disable
之后,您可以确保tasklet
没有在系统中的任何位置运行。void tasklet_disable_nosync(struct tasklet_struct *t);
禁用
tasklet
,但不等待任何当前正在运行的函数退出。当它返回时,tasklet
被禁用,并且在重新使能之前不会被调度,但是,它可以在另一个CPU上运行。void tasklet_enable(struct tasklet_struct *t);
启用先前禁止的
tasklet
。与tasklet_disable
函数成对使用。void tasklet_schedule(struct tasklet_struct *t);
调度
tasklet
执行。如果tasklet
运行之前被调度,只运行一次;如果在运行中被调度,则运行完成后再执行一次。这可确保在处理其它事件时,刚发生的时间能够得到应有的处理。这种行为还允许tasklet
重新调度自己。void tasklet_hi_schedule(struct tasklet_struct *t);
安排
tasklet
以更高的优先级执行。 当软中断处理程序运行时,它会在其它软中断任务(包括“普通”tasklet
)之前处理高优先级的tasklet
。 理想情况下,只有具有低延迟要求的任务(例如填充音频缓冲区)才应使用此功能,以避免其它软中断处理程序引入的额外延迟。 实际上,/proc/jitasklethi
与/proc/jitasklet
之间表现的差异我们是很难看出来的。void tasklet_kill(struct tasklet_struct *t);
“杀死”
tasklet
,保证不会再次运行;通常会在设备被关闭或模块被移除时调用。如果已经运行,则该函数等待直到tasklet
完成执行。在调用该函数之前不能再重新调度自己。
tasklet
相关的实现位于kernel/softirq.c
中。两个tasklet
列表(普通和高优先级)在每个CPU上都会声明, 这点与内核定时器相同。在tasklet
中使用的数据结构是一个单链表,因为它没有内核定时器所需要的排序要求。