小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

中斷下半部分析(tasklet)

 own360 2013-10-10

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

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

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

一、為什么要進入tasklet

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

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

1.       一個實例

#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,在下一次運行softirq時運行這個tasklet*/  

return 0;

}

static void __exit test_exit(void)

{

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

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

}

MODULE_LICENSE(“GPL”);

module_init(test_init);

module_exit(test_exit);

運行結(jié)果如圖:

2.       實現(xiàn)分析

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

在init中,通過函數(shù)tasklet_init()來初始化自己需要注冊到系統(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;

}

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

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

static inline void tasklet_schedule(struct tasklet_struct *t)

{

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

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

__tasklet_schedule(t);

}

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

void fastcall __tasklet_schedule(struct tasklet_struct *t)

{

unsigned long flags;

local_irq_save(flags);

/*把需要添加進系統(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);

}

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

然后通過raise_softirq_irqoff()設(shè)置低優(yōu)先級的tasklet對應的softirq標記,以便cpu在運行softirq的時候運行到tasklet,因為tasklet是凌駕在softirq機制之上的。

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

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

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);中定義了處理tasklet的處理函數(shù)tasklet_action.所以我們要分析這個函數(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)核搶占,

因為內(nèi)核搶占只有兩個時間點: 1.中斷返回到內(nèi)核態(tài);2.手動使能內(nèi)核搶占。

明顯程序員不會在臨界區(qū)內(nèi)手動使能內(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); /*真正運行user注冊的tasklet函數(shù)的地方*/

tasklet_unlock(t);

continue;

}

tasklet_unlock(t);

}

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

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

通過tasklet_schedule()注冊之*/

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對應的softirq,使下次系統(tǒng)運行softirq時能運行到tasklet*/

local_irq_enable();

}

}

運行流程是不是很簡單呢?呵呵。只要注意到加鎖的時機就OK了!

三、總結(jié)

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

本文來自ChinaUnix博客,如果查看原文請點:http://blog./u3/96958/showart_1959111.html

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多