參考:
《Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》
http://blog.csdn.net/fontlose/article/details/8279113
http://blog.chinaunix.net/uid-27212029-id-3386692.html
tasklet是中斷處理下半部分最經(jīng)常使用的一種方法,驅(qū)動程序一般先申請中斷,在中斷處理函數(shù)內(nèi)完畢中斷上半部分的工作后調(diào)用tasklet。tasklet有例如以下特點(diǎn):
1.tasklet僅僅能夠在一個CPU上同步地運(yùn)行,不同的tasklet能夠在不同地CPU上同步地運(yùn)行。
2.tasklet的實(shí)現(xiàn)是建立在兩個軟件中斷的基礎(chǔ)之上的,即HI_SOFTIRQ和TASKLET_SOFTIRQ,本質(zhì)上沒有什么差別,僅僅只是HI_SOFTIRQ的優(yōu)先級更高一些
3.因?yàn)閠asklet是在軟中斷上實(shí)現(xiàn)的,所以像軟中斷一樣不能睡眠、不能堵塞,處理函數(shù)內(nèi)不能含有導(dǎo)致睡眠的動作,如降低信號量、從用戶空間拷貝數(shù)據(jù)或手工分配內(nèi)存等。
4.一個 tasklet 可以被禁止而且之后被又一次使能; 它不會運(yùn)行直到它被使能的次數(shù)與被禁止的次數(shù)同樣.
5.tasklet的串行化使tasklet函數(shù)不必是可重入的,因此簡化了設(shè)備驅(qū)動程序開發(fā)人員的工作。
6.每一個cpu擁有一個tasklet_vec鏈表,詳細(xì)是哪個cpu的tasklet_vec鏈表,是依據(jù)當(dāng)前線程是執(zhí)行在哪個cpu來決定的。
tasklet是驅(qū)動程序?qū)崿F(xiàn)可延遲函數(shù)的首選方法,tasklet建立在HI_SOFTIRT和TASKLET_SOFTIRQ兩個軟中斷上。
原理
tasklet和高優(yōu)先級的tasklet分別存放在tasklet_vec和tasklet_hi_vec數(shù)組中,二者都包括類型為tasklet_head的
NR_CPUS個元素,每一個元素都是指向tasklet描寫敘述符鏈表的指針。
運(yùn)行過程
HI_SOFTIRQ軟中斷相關(guān)的軟中斷函數(shù)是tasklet_hi_action(),而與TASKLET_SOFTIRQ相關(guān)的函數(shù)是tasklet_action()
? ? ?1.禁止本地中斷
? ? ?2.獲得本地CPU的邏輯號n
? ? ?3.把tasklet_vec[n]或tasklet_hi_vec[n]所指向的鏈表的地址存入局部變量list
? ? ?4.把tasklet_vec[n]或tasklet_hi_vec[n]的值賦為NULL,因此已調(diào)度的tasklet描寫敘述符鏈表被清空
? ? ?5.打開本地中斷
? ? ?6.對于list所指向的每一個tasklet描寫敘述符
? ? ? ? ? ? a.在多處理器系統(tǒng)上,檢查tasklet的TASKLET_STATE_RUN標(biāo)志。
? ? ? ? ? ? ? ? ? ? if標(biāo)志被設(shè)置,list又一次插入結(jié)構(gòu)數(shù)組,并激活TASKLET_SOFTIRQ或HI_SOFTIRQ軟中斷,這個tasklet
? ? 被延遲
? ? ? ? ? ? ? ? ? ? else 設(shè)置TASKLET_STATE_RUN標(biāo)志,以便tasklet不能在其它CPU上執(zhí)行 ? ? ? ? ? ?
? ? ? ? ? ? b.通過查看tasklet描寫敘述符的count字段,看tasklet是否被禁止。假設(shè)是清TASKLET_STATE_RUN標(biāo)志,把
? ? list又一次插入結(jié)構(gòu)數(shù)組,并激活對應(yīng)的軟中斷。
? ? ? ? ? ? c.假設(shè)tasklet被激活,清TASKLET_STATE_SCHED標(biāo)志,并運(yùn)行tasklet函數(shù)
編寫一個設(shè)備驅(qū)動程序的步驟
1.分配一個新的tasklet_struct數(shù)據(jù)結(jié)構(gòu),并用tasklet_init()初始化它;
2.實(shí)現(xiàn)tasklet函數(shù)
3.禁止或使能tasklet
tasklet結(jié)構(gòu)體
- struct ?tasklet_struct??
- {??
- ???? struct ?tasklet_struct?*next;??
- ????unsigned? long ?state;??
- ????atomic_t?count;??
- ???? void ?(*func)(unsigned? long );??
- ????unsigned? long ?data;??
- };??
- ??
- tasklet結(jié)構(gòu)變量是tasklet_vec鏈表的一個節(jié)點(diǎn),next是鏈表的下一節(jié)點(diǎn),state使用了兩個位例如以下??
- enum ??
- {??
- ????TASKLET_STATE_SCHED,???? /*?1已經(jīng)被調(diào)度,0表示還沒調(diào)度*/ ??
- ????TASKLET_STATE_RUN??? /*?1tasklet正在運(yùn)行,0表示尚未運(yùn)行,僅僅針對SMP有效,單處理器無意義?*/ ??
- };??
- ??
- count用于禁止使能,每禁止一次計(jì)數(shù)加一,沒使能一次計(jì)數(shù)減一,僅僅有禁止次數(shù)和使能次數(shù)一樣(count等于0)時tasklet才會運(yùn)行調(diào)用函數(shù)。??
- func?運(yùn)行函數(shù)不能有導(dǎo)致睡眠、不能堵塞的代碼。??
- data?運(yùn)行函數(shù)的參數(shù)??
tasklet的定義
- 定義時初始化?????
- ????定義變量名為name的tasklets_struct變量,并初始化調(diào)用函數(shù)為func,參數(shù)為data,使能tasklet??
- ????DECLARE_TASKLET(name,?func,?data);?????#define?DECLARE_TASKLET(name,?func,?data)?\??
- ???? struct ?tasklet_struct?name?=?{?NULL,?0,?ATOMIC_INIT(0),?func,?data?}??
- ??
- ????定義變量名為name的tasklets_struct變量,并初始化調(diào)用函數(shù)為func,參數(shù)為data,禁止tasklet??
- ????DECLARE_TASKLET_DISABLED(name,?func,?data);??
- ????#define?DECLARE_TASKLET_DISABLED(name,?func,?data)?\ ??
- ???? struct ?tasklet_struct?name?=?{?NULL,?0,?ATOMIC_INIT(1),?func,?data?}??
- ??
- 執(zhí)行中初始化????先定義???? struct ?tasklet_struct?name?;??
- ????后初始化????
- ??
- void ?tasklet_init( struct ?tasklet_struct?*t, void ?(*func)(unsigned? long ),?unsigned? long ?data)??
- {??
- ????t->next?=?NULL;?????????????? // ??
- ????t->state?=?0;???????????????? //設(shè)置為未調(diào)度?未執(zhí)行?? ??
- ????atomic_set(&t->count,?0);???? //默認(rèn)使能 ??
- ????t->func?=?func;?????????????? //調(diào)用函數(shù) ??
- ????t->data?=?data;?????????????? //調(diào)用函數(shù)參數(shù) ??
- }??
tasklet的調(diào)用過程
- static ? inline ? void ?tasklet_schedule( struct ?tasklet_struct?*t);使用此函數(shù)就可以完畢調(diào)用??
- static ? inline ? void ?tasklet_schedule( struct ?tasklet_struct?*t)??
- {??
- ???? /*test_and_set_bit設(shè)置調(diào)度位TASKLET_STATE_SCHED,test_and_set_bit返回t->state設(shè)置前狀態(tài),假設(shè)設(shè)置前狀態(tài)為1(已被調(diào)用)那么直接退出否則進(jìn)入__tasklet_schedule函數(shù)*/ ??
- ???? if ?(!test_and_set_bit(TASKLET_STATE_SCHED,?&t->state))??
- ????????__tasklet_schedule(t);??
- }??
- ??
- ??
- void ?fastcall?__tasklet_schedule( struct ?tasklet_struct?*t)??
- {??
- ????unsigned? long ?flags;??
- ????local_irq_save(flags);?????????????????????? //關(guān)中斷保存中斷狀態(tài) ??
- ????t->next?=?__get_cpu_var(tasklet_vec).list;?? //這兩行用于將新插入的節(jié)點(diǎn)?放置在tasklet_vec鏈表的頭部 ??
- ????__get_cpu_var(tasklet_vec).list?=?t;???????? //? ??
- ????raise_softirq_irqoff(TASKLET_SOFTIRQ);?????? //觸發(fā)一個軟終端 ??
- ????local_irq_restore(flags);??????????????????? //使能中斷的同一時候還恢復(fù)了由?local_irq_save()?所保存的中斷狀態(tài) ??
- }??
- 至此調(diào)度函數(shù)已經(jīng)觸發(fā)了一個軟中斷,詳細(xì)中斷函數(shù)看tasklet的初始化??
- void ?__init?softirq_init( void )??
- {??
- ????????open_softirq(TASKLET_SOFTIRQ,?tasklet_action,?NULL); //能夠看到軟中斷觸發(fā)后會運(yùn)行tasklet_action這個函數(shù) ??
- ????????open_softirq(HI_SOFTIRQ,?tasklet_hi_action,?NULL);??
- }??
- ??
- ??
- static ? void ?tasklet_action( struct ?softirq_action?*a)??
- {??
- ???? struct ?tasklet_struct?*list;??
- ??
- ????local_irq_disable();??????????????????????? //這里先關(guān)中斷?保證原子操作 ??
- ????list?=?__get_cpu_var(tasklet_vec).list;???? //取出tasklet_vec鏈表表頭 ??
- ????__get_cpu_var(tasklet_vec).list?=?NULL;???? //由于以下將會一次處理完,這里能夠預(yù)先清空tasklet_vec鏈表,對于為處理完的會又一次增加鏈表 ??
- ??????????????????????????????????????????????? //也能夠?qū)嵢缃駎asklet的處理函數(shù)中又一次增加自己。 ??
- ????local_irq_enable();??
- ??
- ??
- ??
- ???? while ?(list)?{??
- ???????? struct ?tasklet_struct?*t?=?list;??????? //取一節(jié)點(diǎn) ??
- ??
- ????????list?=?list->next;????????????????????? //循環(huán)遍歷所有節(jié)點(diǎn)? ??
- ??
- ???????? if ?(tasklet_trylock(t))?{?????????????? //這里僅僅是測試TASKLET_STATE_RUN標(biāo)記,防止tasklet反復(fù)調(diào)用?? ??
- ??????????????????????????????????????????????? //疑問:這里假設(shè)推斷tasklet已經(jīng)在上執(zhí)行了,trylock失敗,那么為什么后面會被又一次增加鏈表呢,那不是下次又執(zhí)行了? ??
- ???????????? if ?(!atomic_read(&t->count))?{????? //疑問:?假設(shè)tasklet被禁止了那么后面有把它加回鏈表中又一次觸發(fā)一次軟中斷,這樣不是一直有軟中斷了嗎?為什么不在禁止的時候移出鏈表,使能時候在增加呢??? ??
- ???????????????? if ?(!test_and_clear_bit(TASKLET_STATE_SCHED,?&t->state))? //檢查可調(diào)度位是否設(shè)置了,正常應(yīng)該設(shè)置了的 ??
- ?????????????????????BUG();?????????????????????
- ????????????????t->func(t->data);?????????????? //處理調(diào)用函數(shù) ??
- ????????????????tasklet_unlock(t);????????????? //清TASKLET_STATE_RUN標(biāo)記 ??
- ???????????????? continue ;??
- ????????????}??
- ????????????tasklet_unlock(t);??
- ????????}??
- ??
- ????????local_irq_disable();??
- ????????t->next?=?__get_cpu_var(tasklet_vec).list;? //對于trylock失敗和tasklet禁止的節(jié)點(diǎn)會被又一次增加鏈表 ??
- ????????__get_cpu_var(tasklet_vec).list?=?t;??
- ????????__raise_softirq_irqoff(TASKLET_SOFTIRQ);??? //發(fā)起新的軟中斷,這里有兩條鏈表一條是處理中的鏈表list,一個是當(dāng)前tasklet_vec中的鏈表,當(dāng)出現(xiàn)不能處理的節(jié)點(diǎn)時將節(jié)點(diǎn)又一次增加tasklet_vec中后發(fā)起新的軟中斷,那么未處理的節(jié)點(diǎn)也會在下次中斷中處理。 ??
- ????????local_irq_enable();??
- ????}??
- }??
相關(guān)函數(shù)
- /*和tasklet_disable類似,可是tasklet可能仍然執(zhí)行在還有一個?CPU?*/ ??
- static ? inline ? void ?tasklet_disable_nosync( struct ?tasklet_struct?*t)??
- {??
- ????atomic_inc(&t->count);?????? //降低計(jì)數(shù)后,t可能正在執(zhí)行 ??
- ????smp_mb__after_atomic_inc();? //保證在多處理器時同步 ??
- }??
- /*函數(shù)臨時禁止給定的tasklet被tasklet_schedule調(diào)度,直到這個tasklet被再次被enable;若這個tasklet當(dāng)前在執(zhí)行,?這個函數(shù)忙等待直到這個tasklet退出*/ ??
- ??
- static ? inline ? void ?tasklet_disable( struct ?tasklet_struct?*t){??
- ???tasklet_disable_nosync(t);???
- ???tasklet_unlock_wait(t);?? //等待TASKLET——STATE_RUN標(biāo)記清零??? ??
- ???smp_mb();??
- }??
- ??
- static ? inline ? int ?tasklet_trylock( struct ?tasklet_struct?*t){??
- ??? return ?!test_and_set_bit(TASKLET_STATE_RUN,?&(t)->state);??
- }??
- ??
- static ? inline ? void ?tasklet_unlock( struct ?tasklet_struct?*t){?????
- ????????smp_mb__before_clear_bit();???????
- ????????clear_bit(TASKLET_STATE_RUN,?&(t)->state);??
- }??
- ??
- static ? inline ? void ?tasklet_unlock_wait( struct ?tasklet_struct?*t){??
- ???? while ?(test_bit(TASKLET_STATE_RUN,?&(t)->state))?{??
- ??????????barrier();???
- ?????}??
- }??
- ??
- /*使能一個之前被disable的tasklet;若這個tasklet已經(jīng)被調(diào)度,?它會非常快執(zhí)行。tasklet_enable和tasklet_disable必須匹配調(diào)用,?由于內(nèi)核跟蹤每一個tasklet的"禁止次數(shù)"*/ ???
- static ? inline ? void ?tasklet_enable( struct ?tasklet_struct?*t)??
- {??
- ????smp_mb__before_atomic_dec();??
- ????atomic_dec(&t->count);??
- }??
- ??
- /*和tasklet_schedule類似,僅僅是在更高優(yōu)先級執(zhí)行。當(dāng)軟中斷處理執(zhí)行時,?它處理高優(yōu)先級?tasklet?在其它軟中斷之前,僅僅有具有低響應(yīng)周期要求的驅(qū)動才應(yīng)使用這個函數(shù),?可避免其它軟件中斷處理引入的附加周期*/ ??
- void ?tasklet_hi_schedule( struct ?tasklet_struct?*t);??
- ??
- /*確保了?tasklet?不會被再次調(diào)度來執(zhí)行,通常當(dāng)一個設(shè)備正被關(guān)閉或者模塊卸載時被調(diào)用。假設(shè)?tasklet?正在執(zhí)行,?這個函數(shù)等待直到它執(zhí)行完成。若?tasklet?又一次調(diào)度它自己,則必須阻止在調(diào)用?tasklet_kill?前它又一次調(diào)度它自己,如同使用?del_timer_sync*/ ??
- void ?tasklet_kill( struct ?tasklet_struct?*t)??
- {??
- ???? if ?(in_interrupt())??
- ????????printk( "Attempt?to?kill?tasklet?from?interrupt\n" );??
- ??
- ???????? while ?(test_and_set_bit(TASKLET_STATE_SCHED,?&t->state))?{? //檢測t是否被調(diào)度 ??
- ???????? do ??
- ????????????yield();??
- ???????? while ?(test_bit(TASKLET_STATE_SCHED,?&t->state));?????????? //等待t調(diào)度位清零,還未運(yùn)行調(diào)用函數(shù) ??
- ????}??
- ????tasklet_unlock_wait(t);???????????????????????????????????????? //等待t調(diào)用函數(shù)運(yùn)行完 ??
- ????clear_bit(TASKLET_STATE_SCHED,?&t->state);????????????????????? //函數(shù)調(diào)用完可能t被又一次增加鏈表,所以再清一次保證不再調(diào)用 ??
- }??
- 這個函數(shù)不是真的去殺掉被調(diào)度的tasklet,而是保證tasklet不再調(diào)用 ?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

