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
中使用的數據結構是一個單鏈表,因爲它沒有內核定時器所需要的排序要求。