日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

中斷下半部分析(tasklet)

 own360 2013-10-10

------------------------------------------

轉(zhuǎn)載請(qǐng)注明出處:http://lullaby2005./

------------------------------------------

一、為什么要進(jìn)入tasklet

我們?cè)趕oftirq的文章中分析過,在SMP系統(tǒng)中,任何一個(gè)處理器在響應(yīng)外設(shè)中斷請(qǐng)求,完成中斷上半部處理后,都可以調(diào)用函數(shù)do_softirq()來處理構(gòu)建在softirq機(jī)制上的下半部。也就是說,softirq處理函數(shù)在SMP系統(tǒng)中是可以并行執(zhí)行的,這要求使用softirq機(jī)制的下半部必須是多處理器可重入的。這對(duì)于一般的驅(qū)動(dòng)程序開發(fā)者而言, 事情會(huì)變得復(fù)雜化、難度增大。為了降低驅(qū)動(dòng)開發(fā)難度必須提供一套有效的機(jī)制,tasklet就是為了解決這一問題而出現(xiàn)的。

二、tasklet實(shí)現(xiàn)分析

1.       一個(gè)實(shí)例

#include

#include

#include

#include

#include

#include

#include

static struct tasklet_struct my_tasklet;  /*定義自己的tasklet_struct變量*/

static void tasklet_handler (unsigned long data)

{

printk(KERN_ALERT “tasklet_handler is running.\n”);

}

static int __init test_init(void)

{

tasklet_init(&my_tasklet, tasklet_handler, 0); /*掛入鉤子函數(shù)tasklet_handler*/

tasklet_schedule(&my_tasklet); /* 觸發(fā)softirq的TASKLET_SOFTIRQ,在下一次運(yùn)行softirq時(shí)運(yùn)行這個(gè)tasklet*/  

return 0;

}

static void __exit test_exit(void)

{

tasklet_kill(&my_tasklet); /*禁止該tasklet的運(yùn)行*/

printk(KERN_ALERT “test_exit running.\n”);

}

MODULE_LICENSE(“GPL”);

module_init(test_init);

module_exit(test_exit);

運(yùn)行結(jié)果如圖:

2.       實(shí)現(xiàn)分析

我們就從上面這個(gè)實(shí)例入手來分析tasklet的實(shí)現(xiàn),

在init中,通過函數(shù)tasklet_init()來初始化自己需要注冊(cè)到系統(tǒng)中的tasklet結(jié)構(gòu):

void tasklet_init(struct tasklet_struct *t,

void (*func)(unsigned long), unsigned long data)

{

t->next = NULL;

t->state = 0;

atomic_set(&t->count, 0);

t->func = func;

t->data = data;

}

很簡(jiǎn)單,只是初始化tasklet_struct的各個(gè)字段,掛上鉤子函數(shù)。

然后,通過函數(shù)tasklet_schedule()來觸發(fā)該tasklet

static inline void tasklet_schedule(struct tasklet_struct *t)

{

/*如果需要調(diào)度的tasklet的state不為TASKLET_STATE_SCHED,則觸發(fā)之。這樣,就保證了多個(gè)cpu不可能同時(shí)運(yùn)行同一個(gè)tasklet,因?yàn)槿绻粋€(gè)tasklet被調(diào)度過一次,那么它的state字段就會(huì)被設(shè)置TASKLET_STATE_SCHED標(biāo)記,然后插入per-cpu變量的鏈表中。如果這時(shí)另外一個(gè)cpu也去調(diào)度該tasklet,那么就會(huì)在下面的if語(yǔ)句中被擋掉,不會(huì)運(yùn)行到__tasklet_schedule(),從而不會(huì)插入到另外這個(gè)cpu的per-cpu變量的鏈表中,就不會(huì)被運(yùn)行到。所以這里是保證了tasklet編寫的函數(shù)不用是可重入的,這樣就方便了編程人員。(注意,softirq機(jī)制需要編寫可重入的函數(shù))*/

if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))

__tasklet_schedule(t);

}

我們來看__tasklet_schedule()的實(shí)現(xiàn):

void fastcall __tasklet_schedule(struct tasklet_struct *t)

{

unsigned long flags;

local_irq_save(flags);

/*把需要添加進(jìn)系統(tǒng)的自己編寫的struct tasklet_struc加入

到per-cpu變量tasklet_vec的本地副本的鏈表的表頭中*/

t->next = __get_cpu_var(tasklet_vec).list;

__get_cpu_var(tasklet_vec).list = t;

raise_softirq_irqoff(TASKLET_SOFTIRQ); /*觸發(fā)softirq的TASKLET_SOFTIRQ*/  

local_irq_restore(flags);

}

這段代碼也非常簡(jiǎn)單,只是把自己要注冊(cè)到系統(tǒng)中的tasklet_struct掛入到per-cpu變量tasklet_vec的list中而已,這里是掛到鏈表首部。因?yàn)樾枰薷膒er-cpu變量tasklet_vec的list的值,為了防止中斷處理程序也去修改這個(gè)值,所以要加自旋鎖,為了保持?jǐn)?shù)據(jù)的一致性。

然后通過raise_softirq_irqoff()設(shè)置低優(yōu)先級(jí)的tasklet對(duì)應(yīng)的softirq標(biāo)記,以便cpu在運(yùn)行softirq的時(shí)候運(yùn)行到tasklet,因?yàn)閠asklet是凌駕在softirq機(jī)制之上的。

OK,這里就完成了我們自己的my_tasklet的注冊(cè)和觸發(fā)對(duì)應(yīng)的softirq,那我們現(xiàn)在就應(yīng)該分析tasklet的運(yùn)行了。

我們前面提到,tasklet是凌駕在softirq機(jī)制之上的。還記得前面說到了Linux中有六種softirq,優(yōu)先級(jí)最高的是HI_SOFTIRQ,優(yōu)先級(jí)最低的是TASKLET_SOFTIRQ,一般情況下我們是利用TASKLET_SOFTIRQ來實(shí)現(xiàn)tasklet的功能。

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);中定義了處理tasklet的處理函數(shù)tasklet_action.所以我們要分析這個(gè)函數(shù)的實(shí)現(xiàn):

static void tasklet_action(struct softirq_action *a)

{

struct tasklet_struct *list;

/*把per-cpu變量tasklet_vec的本地副本上的list設(shè)置為NULL,

由于這里要修改per-cpu變量,為了防止中斷處理程序

或者內(nèi)核搶占造成該數(shù)據(jù)的不一致性,所以這里禁止中斷再修改數(shù)據(jù)

,然后再開啟中斷.(注意,關(guān)閉本地中斷的副作用就是禁止內(nèi)核搶占,

因?yàn)閮?nèi)核搶占只有兩個(gè)時(shí)間點(diǎn): 1.中斷返回到內(nèi)核態(tài);2.手動(dòng)使能內(nèi)核搶占。

明顯程序員不會(huì)在臨界區(qū)內(nèi)手動(dòng)使能內(nèi)核搶占,所以關(guān)閉本地中斷的

副作用就是禁止內(nèi)核搶占)*/

local_irq_disable();

list = __get_cpu_var(tasklet_vec).list;

__get_cpu_var(tasklet_vec).list = NULL;

local_irq_enable();

/*遍歷tasklet鏈表,讓鏈表上掛入的函數(shù)全部執(zhí)行完成*/

while (list) {

struct tasklet_struct *t = list;

list = list->next;

if (tasklet_trylock(t)) {

if (!atomic_read(&t->count)) {

if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))

BUG();

t->func(t->data); /*真正運(yùn)行user注冊(cè)的tasklet函數(shù)的地方*/

tasklet_unlock(t);

continue;

}

tasklet_unlock(t);

}

/*這里相當(dāng)于把tasklet的list指針從鏈表中后移了(可以自行畫圖分析),

所以剛才運(yùn)行過的tasklet回調(diào)函數(shù)以后不會(huì)再次運(yùn)行,除非用于再次

通過tasklet_schedule()注冊(cè)之*/

local_irq_disable();

t->next = __get_cpu_var(tasklet_vec).list;

__get_cpu_var(tasklet_vec).list = t;

__raise_softirq_irqoff(TASKLET_SOFTIRQ);  /*再一次觸發(fā)tasklet對(duì)應(yīng)的softirq,使下次系統(tǒng)運(yùn)行softirq時(shí)能運(yùn)行到tasklet*/

local_irq_enable();

}

}

運(yùn)行流程是不是很簡(jiǎn)單呢?呵呵。只要注意到加鎖的時(shí)機(jī)就OK了!

三、總結(jié)

Tasklet與一般的softirq的比較重要的一個(gè)區(qū)別在于: softirq處理函數(shù)需要被編寫成可重入的,因?yàn)槎鄠€(gè)cpu可能同時(shí)執(zhí)行同一個(gè)softirq處理函數(shù),為了防止數(shù)據(jù)出現(xiàn)不一致性,所以softirq的處理函數(shù)必須被編寫成可重入。最典型的就是要在softirq處理函數(shù)中用spinlock保護(hù)一些共享資源。而tasklet機(jī)制本身就保證了tasklet處理函數(shù)不會(huì)同時(shí)被多個(gè)cpu調(diào)度到。因?yàn)樵趖asklet_schedule()中,就保證了多個(gè)cpu不可能同時(shí)調(diào)度到同一個(gè)tasklet處理函數(shù),這樣tasklet就不用編寫成可重入的處理函數(shù),這樣就大大減輕了kernel編程人員的負(fù)擔(dān)。

本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog./u3/96958/showart_1959111.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多