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

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
