7.5. Tasklets 機(jī)制

2018-02-24 15:50 更新

7.5.?Tasklets 機(jī)制

另一個(gè)有關(guān)于定時(shí)問題的內(nèi)核設(shè)施是 tasklet 機(jī)制. 它大部分用在中斷管理(我們將在第 10 章再次見到).

tasklet 類似內(nèi)核定時(shí)器在某些方面. 它們一直在中斷時(shí)間運(yùn)行, 它們一直運(yùn)行在調(diào)度它們的同一個(gè) CPU 上, 并且它們接收一個(gè) unsigned long 參數(shù). 不象內(nèi)核定時(shí)器, 但是, 你無法請求在一個(gè)指定的時(shí)間執(zhí)行函數(shù). 通過調(diào)度一個(gè) tasklet, 你簡單地請求它在以后的一個(gè)由內(nèi)核選擇的時(shí)間執(zhí)行. 這個(gè)行為對于中斷處理特別有用, 那里硬件中斷必須被盡快處理, 但是大部分的時(shí)間管理可以安全地延后到以后的時(shí)間. 實(shí)際上, 一個(gè) tasket, 就象一個(gè)內(nèi)核定時(shí)器, 在一個(gè)"軟中斷"的上下文中執(zhí)行(以原子模式), 在使能硬件中斷時(shí)執(zhí)行異步任務(wù)的一個(gè)內(nèi)核機(jī)制.

一個(gè) tasklet 存在為一個(gè)時(shí)間結(jié)構(gòu), 它必須在使用前被初始化. 初始化能夠通過調(diào)用一個(gè)特定函數(shù)或者通過使用某些宏定義聲明結(jié)構(gòu):


#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 提供了許多有趣的特色:

  • 一個(gè) tasklet 能夠被禁止并且之后被重新使能; 它不會(huì)執(zhí)行直到它被使能與被禁止相同的的次數(shù).

  • 如同定時(shí)器, 一個(gè) tasklet 可以注冊它自己.

  • 一個(gè) tasklet 能被調(diào)度來執(zhí)行以正常的優(yōu)先級或者高優(yōu)先級. 后一組一直是首先執(zhí)行.

  • taslet 可能立刻運(yùn)行, 如果系統(tǒng)不在重載下, 但是從不會(huì)晚于下一個(gè)時(shí)鐘嘀噠.

  • 一個(gè) tasklet 可能和其他 tasklet 并發(fā), 但是對它自己是嚴(yán)格地串行的 -- 同樣的 tasklet 從不同時(shí)運(yùn)行在超過一個(gè)處理器上. 同樣, 如已經(jīng)提到的, 一個(gè) tasklet 常常在調(diào)度它的同一個(gè) CPU 上運(yùn)行.

jit 模塊包括 2 個(gè)文件, /proc/jitasklet 和 /proc/jitasklethi, 它返回和在"內(nèi)核定時(shí)器"一節(jié)中介紹過的 /proc/jitimer 同樣的數(shù)據(jù). 當(dāng)你讀其中一個(gè)文件時(shí), 你取回一個(gè) header 和 sixdata 行. 第一個(gè)數(shù)據(jù)行描述了調(diào)用進(jìn)程的上下文, 并且其他的行描述了一個(gè) tasklet 過程連續(xù)運(yùn)行的上下文. 這是一個(gè)在編譯一個(gè)內(nèi)核時(shí)的運(yùn)行例子:


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

如同由上面數(shù)據(jù)所確定的, tasklet 在下一個(gè)時(shí)間嘀噠內(nèi)運(yùn)行只要 CPU 在忙于運(yùn)行一個(gè)進(jìn)程, 但是它立刻被運(yùn)行當(dāng) CPU 處于空閑. 內(nèi)核提供了一套 ksoftirqd 內(nèi)核線程, 每個(gè) CPU 一個(gè), 只是來運(yùn)行 "軟件中斷" 處理, 就像 tasklet_action 函數(shù). 因此, tasklet 的最后 3 個(gè)運(yùn)行在關(guān)聯(lián)到 CPU 0 的 ksoftirqd 內(nèi)核線程的上下文中發(fā)生. jitasklethi 的實(shí)現(xiàn)使用了一個(gè)高優(yōu)先級 tasklet, 在馬上要來的函數(shù)列表中解釋.

jit 中實(shí)現(xiàn) /proc/jitasklet 和 /proc/jittasklethi 的實(shí)際代碼與 /proc/jitimer 的實(shí)現(xiàn)代碼幾乎是一致的, 但是它使用 tasklet 調(diào)用代替那些定時(shí)器. 下面的列表詳細(xì)展開了 tasklet 結(jié)構(gòu)已被初始化后的內(nèi)核對 tasklet 的接口:

void tasklet_disable(struct tasklet_struct *t);
這個(gè)函數(shù)禁止給定的 tasklet. tasklet 可能仍然被 tasklet_schedule 調(diào)度, 但是它的執(zhí)行被延后直到這個(gè) tasklet 被再次使能. 如果這個(gè) tasklet 當(dāng)前在運(yùn)行, 這個(gè)函數(shù)忙等待直到這個(gè)tasklet退出; 因此, 在調(diào)用 tasklet_disable 后, 你可以確保這個(gè) tasklet 在系統(tǒng)任何地方都不在運(yùn)行.

void tasklet_disable_nosync(struct tasklet_struct *t);
禁止這個(gè) tasklet, 但是沒有等待任何當(dāng)前運(yùn)行的函數(shù)退出. 當(dāng)它返回, 這個(gè) tasklt 被禁止并且不會(huì)在以后被調(diào)度直到重新使能, 但是它可能仍然運(yùn)行在另一個(gè) CPU 當(dāng)這個(gè)函數(shù)返回時(shí).

void tasklet_enable(struct tasklet_struct *t);
使能一個(gè)之前被禁止的 tasklet. 如果這個(gè) tasklet 已經(jīng)被調(diào)度, 它會(huì)很快運(yùn)行. 一個(gè)對 tasklet_enable 的調(diào)用必須匹配每個(gè)對 tasklet_disable 的調(diào)用, 因?yàn)閮?nèi)核跟蹤每個(gè) tasklet 的"禁止次數(shù)".

void tasklet_schedule(struct tasklet_struct *t);
調(diào)度 tasklet 執(zhí)行. 如果一個(gè) tasklet 被再次調(diào)度在它有機(jī)會(huì)運(yùn)行前, 它只運(yùn)行一次. 但是, 如果他在運(yùn)行中被調(diào)度, 它在完成后再次運(yùn)行; 這保證了在其他事件被處理當(dāng)中發(fā)生的事件收到應(yīng)有的注意. 這個(gè)做法也允許一個(gè) tasklet 重新調(diào)度它自己.

void tasklet_hi_schedule(struct tasklet_struct *t);
調(diào)度 tasklet 在更高優(yōu)先級執(zhí)行. 當(dāng)軟中斷處理運(yùn)行時(shí), 它處理高優(yōu)先級 tasklet 在其他軟中斷之前, 包括"正常的" tasklet. 理想地, 只有具有低響應(yīng)周期要求( 例如填充音頻緩沖 )應(yīng)當(dāng)使用這個(gè)函數(shù), 為避免其他軟件中斷處理引入的附加周期. 實(shí)際上, /proc/jitasklethi 沒有顯示可見的與 /proc/jitasklet 的區(qū)別.

void tasklet_kill(struct tasklet_struct *t);
這個(gè)函數(shù)確保了這個(gè) tasklet 沒被再次調(diào)度來運(yùn)行; 它常常被調(diào)用當(dāng)一個(gè)設(shè)備正被關(guān)閉或者模塊卸載時(shí). 如果這個(gè) tasklet 被調(diào)度來運(yùn)行, 這個(gè)函數(shù)等待直到它已執(zhí)行. 如果這個(gè) tasklet 重新調(diào)度它自己, 你必須阻止在調(diào)用 tasklet_kill 前它重新調(diào)度它自己, 如同使用 del_timer_sync.

tasklet 在 kernel/softirq.c 中實(shí)現(xiàn). 2 個(gè) tasklet 鏈表( 正常和高優(yōu)先級 )被聲明為每-CPU數(shù)據(jù)結(jié)構(gòu), 使用和內(nèi)核定時(shí)器相同的 CPU-親和 機(jī)制. 在 tasklet 管理中的數(shù)據(jù)結(jié)構(gòu)是簡單的鏈表, 因?yàn)?tasklet 沒有內(nèi)核定時(shí)器的分類請求.

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號