Linux內(nèi)核中斷處理“下半部”機制(上)(超詳細~)
1. 中斷處理“下半部”機制
中斷服務程序一般都是在中斷請求關(guān)閉的條件下執(zhí)行的,以避免嵌套而使中斷控制復雜化。但是,中斷是一個隨機事件,它隨時會到來,如果關(guān)中斷的時間太長,CPU就不能及時響應其他的中斷請求,從而造成中斷的丟失。
因此,Linux內(nèi)核的目標就是盡可能快的處理完中斷請求,盡其所能把更多的處理向后推遲。例如,假設一個數(shù)據(jù)塊已經(jīng)達到了網(wǎng)線,當中斷控制器接受到這個中斷請求信號時,Linux內(nèi)核只是簡單地標志數(shù)據(jù)到來了,然后讓處理器恢復到它以前運行的狀態(tài),其余的處理稍后再進行(如把數(shù)據(jù)移入一個緩沖區(qū),接受數(shù)據(jù)的進程就可以在緩沖區(qū)找到數(shù)據(jù))。
因此,內(nèi)核把中斷處理分為兩部分:上半部(top-half)和下半部(bottom-half),上半部 (就是中斷服務程序)內(nèi)核立即執(zhí)行,而下半部(就是一些內(nèi)核函數(shù))留著稍后處理。
首先:一個快速的“上半部”來處理硬件發(fā)出的請求,它必須在一個新的中斷產(chǎn)生之前終止。通常,除了在設備和一些內(nèi)存緩沖區(qū)(如果你的設備用到了DMA,就不止這些)之間移動或傳送數(shù)據(jù),確定硬件是否處于健全的狀態(tài)之外,這一部分做的工作很少。
第二:“下半部”運行時是允許中斷請求的,而上半部運行時是關(guān)中斷的,這是二者之間的主要區(qū)別。
內(nèi)核到底什么時候執(zhí)行下半部,以何種方式組織下半部?
這就是我們要討論的下半部實現(xiàn)機制,這種機制在內(nèi)核的演變過程中不斷得到改進,在以前的內(nèi)核中,這個機制叫做bottom-half(以下簡稱BH)。但是,Linux的這種bottom-half機制有兩個缺點:
在任意一時刻,系統(tǒng)只能有一個CPU可以執(zhí)行BH代碼,以防止兩個或多個CPU同時來執(zhí)行BH函數(shù)而相互干擾。因此BH代碼的執(zhí)行是嚴格“串行化”的。
BH函數(shù)不允許嵌套。
這兩個缺點在單CPU系統(tǒng)中是無關(guān)緊要的,但在SMP系統(tǒng)中卻是非常致命的。因為BH機制的嚴格串行化執(zhí)行顯然沒有充分利用SMP系統(tǒng)的多CPU特點。為此,在2.4以后的版本中有了新的發(fā)展和改進,改進的目標使下半部可以在多處理機上并行執(zhí)行,并有助于驅(qū)動程序的開發(fā)者進行驅(qū)動程序的開發(fā)。下面主要介紹3種2.6內(nèi)核中的“下半部”處理機制:
軟中斷請求(softirq)機制
小任務(tasklet)機制
工作隊列機制
以上三種機制的比較如下圖所示:

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ? ??


2. 軟中斷請求(softirq)機制
Linux的softirq機制是與SMP緊密不可分的。為此,整個softirq機制的設計與實現(xiàn)中自始自終都貫徹了一個思想:“誰觸發(fā),誰執(zhí)行”(Who marks,Who runs),也即觸發(fā)軟中斷的那個CPU負責執(zhí)行它所觸發(fā)的軟中斷,而且每個CPU都有它自己的軟中斷觸發(fā)與控制機制。這個設計思想也使得softirq機制充分利用了SMP系統(tǒng)的性能和特點。
2.1 軟中斷描述符
Linux在include/linux/interrupt.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)softirq_action,來描述一個軟中斷請求,如下所示:
其中,函數(shù)指針action指向軟中斷請求的服務函數(shù)?;谏鲜鲕浿袛嗝枋龇?,Linux在kernel/softirq.c文件中定義了一個全局的softirq_vec數(shù)組:
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
在這里系統(tǒng)一共定義了10個軟中斷請求描述符。軟中斷向量i(0≤i≤9)所對應的軟中斷請求描述符就是softirq_vec[i]。這個數(shù)組是個系統(tǒng)全局數(shù)組,即它被所有的CPU所共享。這里需要注意的一點是:每個CPU雖然都有它自己的觸發(fā)和控制機制,并且只執(zhí)行他自己所觸發(fā)的軟中斷請求,但是各個CPU所執(zhí)行的軟中斷服務例程卻是相同的,也即都是執(zhí)行softirq_vec[ ]數(shù)組中定義的軟中斷服務函數(shù)。Linux在kernel/softirq.c中的相關(guān)代碼如下:
2.2 軟中斷觸發(fā)機制
要實現(xiàn)“誰觸發(fā),誰執(zhí)行”的思想,就必須為每個CPU都定義它自己的觸發(fā)和控制變量。為此,Linux在include/asm-i386/hardirq.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)irq_cpustat_t來描述一個CPU的中斷統(tǒng)計信息,其中就有用于觸發(fā)和控制軟中斷的成員變量。數(shù)據(jù)結(jié)構(gòu)irq_cpustat_t的定義如下:
IPI: 處理器間的中斷(Inter-Processor Interrupts)
中斷處理的相關(guān)宏如下:
?irq_cpustat_tirq_stat[NR_CPUS] ____cacheline_aligned;
NR_CPUS:為系統(tǒng)中CPU個數(shù)。
這樣,每個CPU都只操作它自己的中斷統(tǒng)計信息結(jié)構(gòu)。假設有一個編號為id的CPU,那么它只能操作它自己的中斷統(tǒng)計信息結(jié)構(gòu)irq_stat[id](0≤id≤NR_CPUS-1),從而使各CPU之間互不影響。
? ?1) 觸發(fā)軟中斷函數(shù):
? ? ? ?void raise_softirq(unsigned int nr);// nr為中斷號
? ?2) 設置軟中斷服務函數(shù):
? ? ? void open_softirq(int nr, void (*action)(struct softirq_action *)); // nr為中斷號, action為中斷處理函數(shù)
2.3 初始化軟中斷(softirq_init)
2.4 軟中斷服務的執(zhí)行函數(shù)do_softirq
函數(shù)do_softirq()負責執(zhí)行數(shù)組softirq_vec[i]中設置的軟中斷服務函數(shù)。每個CPU都是通過執(zhí)行這個函數(shù)來執(zhí)行軟中斷服務的。由于同一個CPU上的軟中斷服務例程不允許嵌套,因此,do_softirq()函數(shù)一開始就檢查當前CPU是否已經(jīng)正出在中斷服務中,如果是則do_softirq()函數(shù)立即返回。舉個例子,假設CPU0正在執(zhí)行do_softirq()函數(shù),執(zhí)行過程產(chǎn)生了一個高優(yōu)先級的硬件中斷,于是CPU0轉(zhuǎn)去執(zhí)行這個高優(yōu)先級中斷所對應的中斷服務程序。眾所周知,所有的中斷服務程序最后都要跳轉(zhuǎn)到do_IRQ()函數(shù)并由它來依次執(zhí)行中斷服務隊列中的ISR,這里我們假定這個高優(yōu)先級中斷的ISR請求觸發(fā)了一次軟中斷,于是do_IRQ()函數(shù)在退出之前看到有軟中斷請求,從而調(diào)用do_softirq()函數(shù)來服務軟中斷請求。因此,CPU0再次進入do_softirq()函數(shù)(也即do_softirq()函數(shù)在CPU0上被重入了)。但是在這一次進入do_softirq()函數(shù)時,它馬上發(fā)現(xiàn)CPU0此前已經(jīng)處在中斷服務狀態(tài)中了,因此這一次do_softirq()函數(shù)立即返回。于是,CPU0回到該開始時的do_softirq()函數(shù)繼續(xù)執(zhí)行,并為高優(yōu)先級中斷的ISR所觸發(fā)的軟中斷請求補上一次服務。從這里可以看出,do_softirq()函數(shù)在同一個CPU上的執(zhí)行是串行的。
3.小任務( tasklet)機制
tasklet機制是一種較為特殊的軟中斷。
tasklet一詞的原意是“小片任務”的意思,這里是指一小段可執(zhí)行的代碼,且通常以函數(shù)的形式出現(xiàn)。軟中斷向量HI_SOFTIRQ和TASKLET_SOFTIRQ均是用tasklet機制來實現(xiàn)的。
從某種程度上講,tasklet機制是Linux內(nèi)核對BH機制的一種擴展。在2.4內(nèi)核引入了softirq機制后,原有的BH機制正是通過tasklet機制這個橋梁來將softirq機制納入整體框架中的。正是由于這種歷史的延伸關(guān)系,使得tasklet機制與一般意義上的軟中斷有所不同,而呈現(xiàn)出以下兩個顯著的特點:
與一般的軟中斷不同,某一段tasklet代碼在某個時刻只能在一個CPU上運行,而不像一般的軟中斷服務函數(shù)(即softirq_action結(jié)構(gòu)中的action函數(shù)指針)那樣——在同一時刻可以被多個CPU并發(fā)地執(zhí)行。
與BH機制不同,不同的tasklet代碼在同一時刻可以在多個CPU上并發(fā)地執(zhí)行,而不像BH機制那樣必須嚴格地串行化執(zhí)行(也即在同一時刻系統(tǒng)中只能有一個CPU執(zhí)行BH函數(shù))。
3.1 tasklet描述符
Linux用數(shù)據(jù)結(jié)構(gòu)tasklet_struct來描述一個tasklet,每個結(jié)構(gòu)代表一個獨立的小任務。該數(shù)據(jù)結(jié)構(gòu)定義在include/linux/interrupt.h頭文件中。如下所示:
next: 指向下一個tasklet的指針
state: 定義了這個tasklet的當前狀態(tài)。這一個32位的無符號長整數(shù),當前只使用了bit[1]和bit[0]兩個狀態(tài)位。其中,bit[1]=1 表示這個tasklet當前正在某個CPU上被執(zhí)行,它僅對SMP系統(tǒng)才有意義,其作用就是為了防止多個CPU同時執(zhí)行一個tasklet的情形出現(xiàn);bit[0]=1表示這個tasklet已經(jīng)被調(diào)度去等待執(zhí)行了。
對這兩個狀態(tài)位的宏定義如下所示(interrupt.h):
count: 子計數(shù)count,對這個tasklet的引用計數(shù)值。
注:只有當count等于0時,tasklet代碼段才能執(zhí)行,也即此時tasklet是被使能的;如果count非零,則這個tasklet是被禁止的。任何想要執(zhí)行一個tasklet代碼段的人都首先必須先檢查其count成員是否為0。
func:指向以函數(shù)形式表現(xiàn)的可執(zhí)行tasklet代碼段。
data:函數(shù)func的參數(shù)。這是一個32位的無符號整數(shù),其具體含義可供func函數(shù)自行解釋,比如將其解釋成一個指向某個用戶自定義數(shù)據(jù)結(jié)構(gòu)的地址值。
Linux在interrupt.h頭文件中又定義了兩個用來定義tasklet_struct結(jié)構(gòu)變量的輔助宏:
顯然,從上述源代碼可以看出,用DECLARE_TASKLET宏定義的tasklet在初始化時是被使能的(enabled),因為其count成員為0。而用DECLARE_TASKLET_DISABLED宏定義的tasklet在初始時是被禁止的(disabled),因為其count等于1。
3.2 改變一個tasklet狀態(tài)的操作
在這里,tasklet狀態(tài)指兩個方面:
state:成員所表示的運行狀態(tài);
count:成員決定的使能/禁止狀態(tài)。
3.2.1 改變一個tasklet的運行狀態(tài)
state成員中的bit[0]表示一個tasklet是否已被調(diào)度去等待執(zhí)行,bit[1]表示一個tasklet是否正在某個CPU上執(zhí)行。對于state變量中某位的改變必須是一個原子操作,因此可以用定義在include/asm/bitops.h頭文件中的位操作來進行。
由于bit[1]這一位(即TASKLET_STATE_RUN)僅僅對于SMP系統(tǒng)才有意義,因此Linux在Interrupt.h頭文件中顯示地定義了對TASKLET_STATE_RUN位的操作。如下所示:
顯然,在SMP系統(tǒng)同,tasklet_trylock()宏將把一個tasklet_struct結(jié)構(gòu)變量中的state成員中的bit[1]位設置成1,同時還返回bit[1]位的非。因此,如果bit[1]位原有值為1(表示另外一個CPU正在執(zhí)行這個tasklet代碼),那么tasklet_trylock()宏將返回值0,也就表示上鎖不成功。如果bit[1]位的原有值為0,那么tasklet_trylock()宏將返回值1,表示加鎖成功。而在單CPU系統(tǒng)中,tasklet_trylock()宏總是返回為1。
任何想要執(zhí)行某個tasklet代碼的程序都必須首先調(diào)用宏tasklet_trylock()來試圖對這個tasklet進行上鎖(即設置TASKLET_STATE_RUN位),且只能在上鎖成功的情況下才能執(zhí)行這個tasklet。建議!即使你的程序只在CPU系統(tǒng)上運行,你也要在執(zhí)行tasklet之前調(diào)用tasklet_trylock()宏,以便使你的代碼獲得良好可移植性。
在SMP系統(tǒng)中,tasklet_unlock_wait()宏將一直不停地測試TASKLET_STATE_RUN位的值,直到該位的值變?yōu)?(即一直等待到解鎖),假如:CPU0正在執(zhí)行tasklet A的代碼,在此期間,CPU1也想執(zhí)行tasklet A的代碼,但CPU1發(fā)現(xiàn)tasklet A的TASKLET_STATE_RUN位為1,于是它就可以通過tasklet_unlock_wait()宏等待tasklet A被解鎖(也即TASKLET_STATE_RUN位被清零)。在單CPU系統(tǒng)中,這是一個空操作。
宏tasklet_unlock()用來對一個tasklet進行解鎖操作,也即將TASKLET_STATE_RUN位清零。在單CPU系統(tǒng)中,這是一個空操作。
3.2.2 使能/禁止一個tasklet
使能與禁止操作往往總是成對地被調(diào)用的,tasklet_disable()函數(shù)如下(interrupt.h):
函數(shù)tasklet_disable_nosync()也是一個靜態(tài)inline函數(shù),它簡單地通過原子操作將count成員變量的值減1。如下所示(interrupt.h):
函數(shù)tasklet_enable()用于使能一個tasklet,如下所示(interrupt.h):
3.3 tasklet描述符的初始化與殺死
函數(shù)tasklet_init()用來初始化一個指定的tasklet描述符,其源碼如下所示(kernel/softirq.c):
函數(shù)tasklet_kill()用來將一個已經(jīng)被調(diào)度了的tasklet殺死,即將其恢復到未調(diào)度的狀態(tài)。其源碼如下所示(kernel/softirq.c):
3.4 tasklet對列
多個tasklet可以通過tasklet描述符中的next成員指針鏈接成一個單向?qū)α?。為此,Linux專門在頭文件include/linux/interrupt.h中定義了數(shù)據(jù)結(jié)構(gòu)tasklet_head來描述一個tasklet對列的頭部指針。如下所示:
盡管tasklet機制是特定于軟中斷向量HI_SOFTIRQ和TASKLET_SOFTIRQ的一種實現(xiàn),但是tasklet機制仍然屬于softirq機制的整體框架范圍內(nèi)的,因此,它的設計與實現(xiàn)仍然必須堅持“誰觸發(fā),誰執(zhí)行”的思想。為此,Linux為系統(tǒng)中的每一個CPU都定義了一個tasklet對列頭部,來表示應該有各個CPU負責執(zhí)行的tasklet對列。如下所示(kernel/softirq.c):
即: struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned; struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
其中,tasklet_vec[]數(shù)組用于軟中斷向量TASKLET_SOFTIRQ,而tasklet_hi_vec[]數(shù)組則用于軟中斷向量HI_SOFTIRQ。也即,如果CPUi(0≤i≤NR_CPUS-1)觸發(fā)了軟中斷向量TASKLET_SOFTIRQ,那么對列tasklet_vec[i]中的每一個tasklet都將在CPUi服務于軟中斷向量TASKLET_SOFTIRQ時被CPUi所執(zhí)行。同樣地,如果CPUi(0≤i≤NR_CPUS-1)觸發(fā)了軟中斷向量HI_SOFTIRQ,那么隊列tasklet_hi_vec[i]中的每一個tasklet都將CPUi在對軟中斷向量HI_SOFTIRQ進行服務時被CPUi所執(zhí)行。
隊列tasklet_vec[I]和tasklet_hi_vec[I]中的各個tasklet是怎樣被所CPUi所執(zhí)行的呢?其關(guān)鍵就是軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ的軟中斷服務程序——tasklet_action()函數(shù)和tasklet_hi_action()函數(shù)。下面我們就來分析這兩個函數(shù)。
3.5 軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ
Linux為軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ實現(xiàn)了專用的觸發(fā)函數(shù)和軟中斷服務函數(shù)。
1.專用的觸發(fā)函數(shù)
tasklet_schedule()函數(shù)和tasklet_hi_schedule()函數(shù)分別用來在當前CPU上觸發(fā)軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ,并把指定的tasklet加入當前CPU所對應的tasklet隊列中去等待執(zhí)行。
2.專用的軟中斷服務函數(shù)
tasklet_action()函數(shù)和tasklet_hi_action()函數(shù)則分別是軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ的軟中斷服務函數(shù)。在初始化函數(shù)softirq_init()中,這兩個軟中斷向量對應的描述符softirq_vec[0]和softirq_vec[6]中的action函數(shù)指針就被分別初始化成指向函數(shù)tasklet_hi_action()和函數(shù)tasklet_action()。
3.5.1 軟中斷向量TASKLET_SOFTIRQ的觸發(fā)函數(shù)tasklet_schedule
該函數(shù)實現(xiàn)在include/linux/interrupt.h頭文件中,是一個inline函數(shù)。其源碼如下所示:
調(diào)用test_and_set_bit()函數(shù)將待調(diào)度的tasklet的state成員變量的bit[0]位(也即TASKLET_STATE_SCHED位)設置為1,該函數(shù)同時還返回TASKLET_STATE_SCHED位的原有值。因此如果bit[0]為的原有值已經(jīng)為1,那就說明這個tasklet已經(jīng)被調(diào)度到另一個CPU上去等待執(zhí)行了。由于一個tasklet在某一個時刻只能由一個CPU來執(zhí)行,因此tasklet_schedule()函數(shù)什么也不做就直接返回了。否則,就繼續(xù)下面的調(diào)度操作。
首先,調(diào)用local_irq_save()函數(shù)來關(guān)閉當前CPU的中斷,以保證下面的步驟在當前CPU上原子地被執(zhí)行。
然后,將待調(diào)度的tasklet添加到當前CPU對應的tasklet隊列的尾部。
接著,調(diào)用raise_softirq_irqoff函數(shù)在當前CPU上觸發(fā)軟中斷請求TASKLET_SOFTIRQ。
最后,調(diào)用local_irq_restore()函數(shù)來開當前CPU的中斷。
3.5.2 軟中斷向量TASKLET_SOFTIRQ的服務程序tasklet_action
函數(shù)tasklet_action()是tasklet機制與軟中斷向量TASKLET_SOFTIRQ的聯(lián)系紐帶。正是該函數(shù)將當前CPU的tasklet隊列中的各個tasklet放到當前CPU上來執(zhí)行的。該函數(shù)實現(xiàn)在kernel/softirq.c文件中,其源代碼如下:
首先,在當前CPU關(guān)中斷的情況下,“原子”地讀取當前CPU的tasklet隊列頭部指針,將其保存到局部變量list指針中,然后將當前CPU的tasklet隊列頭部指針設置為NULL,以表示理論上當前CPU將不再有tasklet需要執(zhí)行(但最后的實際結(jié)果卻并不一定如此,下面將會看到)。
然后,用一個while{}循環(huán)來遍歷由list所指向的tasklet隊列,隊列中的各個元素就是將在當前CPU上執(zhí)行的tasklet。循環(huán)體的執(zhí)行步驟如下:
用指針t來表示當前隊列元素,即當前需要執(zhí)行的tasklet。
更新list指針為list->next,使它指向下一個要執(zhí)行的tasklet。
用tasklet_trylock()宏試圖對當前要執(zhí)行的tasklet(由指針t所指向)進行加鎖,如果加鎖成功(當前沒有任何其他CPU正在執(zhí)行這個tasklet),則用原子讀函數(shù)atomic_read()進一步判斷count成員的值。如果count為0,說明這個tasklet是允許執(zhí)行的,于是:
? ? ? ? (1) 先清除TASKLET_STATE_SCHED位;
? ? ? ? (2) 然后,調(diào)用這個tasklet的可執(zhí)行函數(shù)func;
? ? ? ? (3) 調(diào)用宏tasklet_unlock()來清除TASKLET_STATE_RUN位
? ? ? ? (4) 最后,執(zhí)行continue語句跳過下面的步驟,回到while循環(huán)繼續(xù)遍歷隊列中的下一個元素。如果count不為0,說明這個tasklet是禁止運行的,于是調(diào)用tasklet_unlock()清除前面用tasklet_trylock()設置的TASKLET_STATE_RUN位。
3.6 tasklet使用總結(jié)
1.聲明和使用小任務大多數(shù)情況下,為了控制一個常用的硬件設備,小任務機制是實現(xiàn)下半部的最佳選擇。小任務可以動態(tài)創(chuàng)建,使用方便,執(zhí)行起來也比較快。我們既可以靜態(tài)地創(chuàng)建小任務,也可以動態(tài)地創(chuàng)建它。選擇那種方式取決于到底是想要對小任務進行直接引用還是一個間接引用。如果準備靜態(tài)地創(chuàng)建一個小任務(也就是對它直接引用),使用下面兩個宏中的一個:
? ? ? DECLARE_TASKLET(name,func, data) ? ? ? DECLARE_TASKLET_DISABLED(name,func, data)
這兩個宏都能根據(jù)給定的名字靜態(tài)地創(chuàng)建一個tasklet_struct結(jié)構(gòu)。當該小任務被調(diào)度以后,給定的函數(shù)func會被執(zhí)行,它的參數(shù)由data給出。這兩個宏之間的區(qū)別在于引用計數(shù)器的初始值設置不同。第一個宏把創(chuàng)建的小任務的引用計數(shù)器設置為0,因此,該小任務處于激活狀態(tài)。另一個把引用計數(shù)器設置為1,所以該小任務處于禁止狀態(tài)。例如:
? ? ? ?DECLARE_TASKLET(my_tasklet,my_tasklet_handler, dev); ? ? ? ?這行代碼其實等價于 ? ? ? ?struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0), ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?tasklet_handler,dev};
這樣就創(chuàng)建了一個名為my_tasklet的小任務,其處理程序為tasklet_handler,并且已被激活。當處理程序被調(diào)用的時候,dev就會被傳遞給它。
2.編寫自己的小任務處理程序小任務處理程序必須符合如下的函數(shù)類型:
? ? ? ? ? ?void ?tasklet_handler(unsigned long data)
由于小任務不能睡眠,因此不能在小任務中使用信號量或者其它產(chǎn)生阻塞的函數(shù)。但是小任務運行時可以響應中斷。
3.調(diào)度自己的小任務通過調(diào)用tasklet_schedule()函數(shù)并傳遞給它相應的tasklt_struct指針,該小任務就會被調(diào)度以便適當?shù)臅r候執(zhí)行:
? ? ? ? ? ?tasklet_schedule(&my_tasklet); ? ? ? ?/*把my_tasklet標記為掛起 */
在小任務被調(diào)度以后,只要有機會它就會盡可能早的運行。在它還沒有得到運行機會之前,如果一個相同的小任務又被調(diào)度了,那么它仍然只會運行一次。
可以調(diào)用tasklet_disable()函數(shù)來禁止某個指定的小任務。如果該小任務當前正在執(zhí)行,這個函數(shù)會等到它執(zhí)行完畢再返回。調(diào)用tasklet_enable()函數(shù)可以激活一個小任務,如果希望把以DECLARE_TASKLET_DISABLED()創(chuàng)建的小任務激活,也得調(diào)用這個函數(shù),如:
? ? ? ? ? tasklet_disable(&my_tasklet); ? ? ? ?/*小任務現(xiàn)在被禁止,這個小任務不能運行*/ ? ? ? ? ?tasklet_enable(&my_tasklet); ? ? ? ?/* ?小任務現(xiàn)在被激活*/
也可以調(diào)用tasklet_kill()函數(shù)從掛起的隊列中去掉一個小任務。該函數(shù)的參數(shù)是一個指向某個小任務的tasklet_struct的長指針。在小任務重新調(diào)度它自身的時候,從掛起的隊列中移去已調(diào)度的小任務會很有用。這個函數(shù)首先等待該小任務執(zhí)行完畢,然后再將它移去。
4.tasklet的簡單用法
下面是tasklet的一個簡單應用,以模塊的形成加載。
從這個例子可以看出,所謂的小任務機制是為下半部函數(shù)的執(zhí)行提供了一種執(zhí)行機制,也就是說,推遲處理的事情是由tasklet_handler實現(xiàn),何時執(zhí)行,經(jīng)由小任務機制封裝后交給內(nèi)核去處理。
由于篇幅有限下文繼續(xù)講解Linux內(nèi)核中斷處理“下半部”機制(下)(超詳細~)
