1. 程式人生 > >softirq(軟中斷)下半部中tasklet與workqueue的區別,整合

softirq(軟中斷)下半部中tasklet與workqueue的區別,整合

一、中斷處理的tasklet(小任務)機制

中斷服務程式一般都是在中斷請求關閉的條件下執行的,以避免巢狀而使中斷控制複雜化。但是,中斷是一個隨機事件,它隨時會到來,如果關中斷的時間太長,CPU就不能及時響應其他的中斷請求,從而造成中斷的丟失。因此,Linux核心的目標就是儘可能快的處理完中斷請求,盡其所能把更多的處理向後推遲。例如,假設一個數據塊已經達到了網線,當中斷控制器接受到這個中斷請求訊號時,Linux核心只是簡單地標誌資料到來了,然後讓處理器恢復到它以前執行的狀態,其餘的處理稍後再進行(如把資料移入一個緩衝區,接受資料的程序就可以在緩衝區找到資料)。因此,核心把中斷處理分為兩部分:上半部(tophalf)和下半部(bottomhalf),上半部(就是中斷服務程式)核心立即執行,而下半部(就是一些核心函式)留著稍後處理,

首先,一個快速的“上半部”來處理硬體發出的請求,它必須在一個新的中斷產生之前終止。通常,除了在裝置和一些記憶體緩衝區(如果你的裝置用到了DMA,就不止這些)之間移動或傳送資料,確定硬體是否處於健全的狀態之外,這一部分做的工作很少。

下半部執行時是允許中斷請求的,而上半部執行時是關中斷的,這是二者之間的主要區別。

但是,核心到底什時候執行下半部,以何種方式組織下半部?這就是我們要討論的下半部實現機制,這種機制在核心的演變過程中不斷得到改進,在以前的核心中,這個機制叫做bottomhalf(簡稱bh),在2.4以後的版本中有了新的發展和改進,改進的目標使下半部可以在多處理機上並行執行,並有助於驅動程式的開發者進行驅動程式的開發。下面主要介紹常用的小任務(Tasklet)機制及2.6核心中的工作佇列機制。


小任務機制

這裡的小任務是指對要推遲執行的函式進行組織的一種機制。其資料結構為tasklet_struct,每個結構代表一個獨立的小任務,其定義如下:

struct tasklet_struct {
struct tasklet_struct *next; /*指向連結串列中的下一個結構*/
unsignedlong state; /* 小任務的狀態*/
atomic_tcount; /* 引用計數器*/
void(*func) (unsigned long); /* 要呼叫的函式*/
unsignedlong data; /* 傳遞給函式的引數*/
};


結構中的func域就是下半部中要推遲執行的函式,data是它唯一的引數。
State域的取值為TASKLET_STATE_SCHED或TASKLET_STATE_RUN。TASKLET_STATE_SCHED表示小任務已被排程,正準備投入執行,TASKLET_STATE_RUN表示小任務正在執行。TASKLET_STATE_RUN只有在多處理器系統上才使用,單處理器系統什麼時候都清楚一個小任務是不是正在執行(它要麼就是當前正在執行的程式碼,要麼不是)。
Count域是小任務的引用計數器。如果它不為0,則小任務被禁止,不允許執行;只有當它為零,小任務才被啟用,並且在被設定為掛起時,小任務才能夠執行。
1. 宣告和使用小任務大多數情況下,為了控制一個尋常的硬體裝置,小任務機制是實現下半部的最佳選擇。小任務可以動態建立,使用方便,執行起來也比較快。
我們既可以靜態地建立小任務,也可以動態地建立它。選擇那種方式取決於到底是想要對小任務進行直接引用還是一個間接引用。如果準備靜態地建立一個小任務(也就是對它直接引用),使用下面兩個巨集中的一個:
DECLARE_TASKLET(name,func, data)
DECLARE_TASKLET_DISABLED(name,func, data)
這兩個巨集都能根據給定的名字靜態地建立一個tasklet_struct結構。當該小任務被排程以後,給定的函式func會被執行,它的引數由data給出。這兩個巨集之間的區別在於引用計數器的初始值設定不同。第一個巨集把建立的小任務的引用計數器設定為0,因此,該小任務處於啟用狀態。另一個把引用計數器設定為1,所以該小任務處於禁止狀態。例如:
DECLARE_TASKLET(my_tasklet,my_tasklet_handler, dev);
這行程式碼其實等價於
structtasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0),
tasklet_handler,dev};
這樣就建立了一個名為my_tasklet的小任務,其處理程式為tasklet_handler,並且已被啟用。當處理程式被呼叫的時候,dev就會被傳遞給它。
2. 編寫自己的小任務處理程式小任務處理程式必須符合如下的函式型別:
voidtasklet_handler(unsigned long data)
由於小任務不能睡眠,因此不能在小任務中使用訊號量或者其它產生阻塞的函式。但是小任務執行時可以響應中斷。
3. 排程自己的小任務通過呼叫tasklet_schedule()函式並傳遞給它相應的tasklt_struct指標,該小任務就會被排程以便適當的時候執行:
tasklet_schedule(&my_tasklet); /*把my_tasklet標記為掛起 */
在小任務被排程以後,只要有機會它就會盡可能早的執行。在它還沒有得到執行機會之前,如果一個相同的小任務又被排程了,那麼它仍然只會執行一次。
可以呼叫tasklet_disable()函式來禁止某個指定的小任務。如果該小任務當前正在執行,這個函式會等到它執行完畢再返回。呼叫tasklet_enable()函式可以啟用一個小任務,如果希望把以DECLARE_TASKLET_DISABLED()建立的小任務啟用,也得呼叫這個函式,如:
tasklet_disable(&my_tasklet); /*小任務現在被禁止,這個小任務不能執行*/
tasklet_enable(&my_tasklet); /* 小任務現在被啟用*/
也可以呼叫tasklet_kill()函式從掛起的佇列中去掉一個小任務。該函式的引數是一個指向某個小任務的tasklet_struct的長指標。在小任務重新排程它自身的時候,從掛起的佇列中移去已排程的小任務會很有用。這個函式首先等待該小任務執行完畢,然後再將它移去。
4.tasklet的簡單用法
下面是tasklet的一個簡單應用,以模組的形成載入。
#include <linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include<linux/interrupt.h>

static struct t asklet_struct my_tasklet;

static void tasklet_handler (unsigned longd ata)
{
printk(KERN_ALERT,"tasklet_handler is running./n");
}

staticint __init test_init(void)
{
tasklet_init(&my_tasklet,tasklet_handler,0);
tasklet_schedule(&my_tasklet);
return0;
}

static void __exit test_exit(void)
{
tasklet_kill(&tasklet);
printk(KERN_ALERT,"test_exit is running./n");
}
MODULE_LICENSE("GPL");

module_init(test_init);
module_exit(test_exit);

從這個例子可以看出,所謂的小任務機制是為下半部函式的執行提供了一種執行機制,也就是說,推遲處理的事情是由tasklet_handler實現,何時執行,經由小任務機制封裝後交給核心去處理。

二、中斷處理的工作佇列機制

工作佇列(work queue)是另外一種將工作推後執行的形式,它和前面討論的tasklet有所不同。工作佇列可以把工作推後,交由一個核心執行緒去執行,也就是說,這個下半部分可以在程序上下文中執行。這樣,通過工作佇列執行的程式碼能佔盡程序上下文的所有優勢。最重要的就是工作佇列允許被重新排程甚至是睡眠。

那麼,什麼情況下使用工作佇列,什麼情況下使用tasklet。如果推後執行的任務需要睡眠,那麼就選擇工作佇列。如果推後執行的任務不需要睡眠,那麼就選擇tasklet。另外,如果需要用一個可以重新排程的實體來執行你的下半部處理,也應該使用工作佇列。它是唯一能在程序上下文執行的下半部實現的機制,也只有它才可以睡眠。這意味著在需要獲得大量的記憶體時、在需要獲取訊號量時,在需要執行阻塞式的I/O操作時,它都會非常有用。如果不需要用一個核心執行緒來推後執行工作,那麼就考慮使用tasklet。

  1. 工作、工作佇列和工作者執行緒

如前所述,我們把推後執行的任務叫做工作(work),描述它的資料結構為work_struct,這些工作以佇列結構組織成工作佇列(workqueue),其資料結構為workqueue_struct,而工作執行緒就是負責執行工作佇列中的工作。系統預設的工作者執行緒為events,自己也可以建立自己的工作者執行緒。

  1. 表示工作的資料結構

工作用<linux/workqueue.h>中定義的work_struct結構表示:

struct work_struct{
unsigned long pending; /* 這個工作正在等待處理嗎?*/
struct list_head entry; /* 連線所有工作的連結串列 */
void (*func) (void *); /* 要執行的函式 */
void *data; /* 傳遞給函式的引數 */
void *wq_data; /* 內部使用 */
struct timer_list timer; /* 延遲的工作佇列所用到的定時器 */
};

這些結構被連線成連結串列。當一個工作者執行緒被喚醒時,它會執行它的連結串列上的所有工作。工作被執行完畢,它就將相應的work_struct物件從連結串列上移去。當連結串列上不再有物件的時候,它就會繼續休眠。

3. 建立推後的工作

要使用工作佇列,首先要做的是建立一些需要推後完成的工作。可以通過DECLARE_WORK在編譯時靜態地建該結構:

DECLARE_WORK(name, void (*func) (void *), void *data);

這樣就會靜態地建立一個名為name,待執行函式為func,引數為data的work_struct結構。

同樣,也可以在執行時通過指標建立一個工作:

INIT_WORK(struct work_struct *work, woid(*func) (void *), void *data);

這會動態地初始化一個由work指向的工作。

4. 工作佇列中待執行的函式

工作佇列待執行的函式原型是:

void work_handler(void *data)

這個函式會由一個工作者執行緒執行,因此,函式會執行在程序上下文中。預設情況下,允許響應中斷,並且不持有任何鎖。如果需要,函式可以睡眠。需要注意的是,儘管該函式執行在程序上下文中,但它不能訪問使用者空間,因為核心執行緒在使用者空間沒有相關的記憶體對映。通常在系統呼叫發生時,核心會代表使用者空間的程序執行,此時它才能訪問使用者空間,也只有在此時它才會對映使用者空間的記憶體。

5. 對工作進行排程

現在工作已經被建立,我們可以排程它了。想要把給定工作的待處理函式提交給預設的events工作執行緒,只需呼叫

schedule_work(&work);

work馬上就會被排程,一旦其所在的處理器上的工作者執行緒被喚醒,它就會被執行。

有時候並不希望工作馬上就被執行,而是希望它經過一段延遲以後再執行。在這種情況下,可以排程它在指定的時間執行:

schedule_delayed_work(&work, delay);

這時,&work指向的work_struct直到delay指定的時鐘節拍用完以後才會執行。

6. 工作佇列的簡單應用

#include<linux/module.h>
#include<linux/init.h>
#include<linux/workqueue.h>

staticstruct workqueue_struct *queue =NULL;
staticstruct work_struct work;

staticvoid work_handler(struct work_struct*data)
{
printk(KERN_ALERT"work handler function./n");
}

staticint __init test_init(void)
{
queue= create_singlethread_workqueue("helloworld"); /*建立一個單執行緒的工作佇列*/
if(!queue)
goto err;

INIT_WORK(&work, work_handler);
schedule_work(&work);/*schedule_work是新增到系統的events workqueue, 要新增到自己的workqueue, 應該使用queue_work, 故此處有誤*/

return 0;
err:
return-1;
}

staticvoid __exit test_exit(void)
{
destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);

tasklet

Workqueue

處於atomic context,不能sleep

不處於atomic context,可以sleep

處於中斷上下文,OS不可以進行程序排程

處於程序上下文,OS可以進行程序排程

執行排程它們的同一個CPU上

默認同一個CPU上

不能指定確定時間進行排程

不能指定確定時間進行排程或者指定至少延時一個確定時間後排程

只能交給ksoftirqd/0

可以提交給events/0,也可以提交給自定義的workqueue

Tasklet函式帶引數

Work函式不帶引數

Tasklet與workqueue的不同應用環境總結如下:

(1) 必須立即進行緊急處理的極少量任務放入在中斷的頂半部中,此時遮蔽了與自己同類型的中斷,由於任務量少,所以可以迅速不受打擾地處理完緊急任務。

(2) 需要較少時間的中等數量的急迫任務放在tasklet中。此時不會遮蔽任何中斷(包括與自己的頂半部同類型的中斷),所以不影響頂半部對緊急事務的處理;同時又不會進行使用者程序排程,從而保證了自己急迫任務得以迅速完成。

(3) 需要較多時間且並不急迫(允許被作業系統剝奪執行權)的大量任務放在workqueue中。此時作業系統會盡量快速處理完這個任務,但如果任務量太大,期間作業系統也會有機會排程別的使用者程序執行,從而保證不會因為這個任務需要執行時間將其它使用者程序無法進行。

(4) 可能引起睡眠的任務放在workqueue中。因為在workqueue中睡眠是安全的。


相關推薦

softirq(中斷)半部taskletworkqueue區別整合

一、中斷處理的tasklet(小任務)機制 中斷服務程式一般都是在中斷請求關閉的條件下執行的,以避免巢狀而使中斷控制複雜化。但是,中斷是一個隨機事件,它隨時會到來,如果關中斷的時間太長,CPU就不能及

linux裝置驅動歸納總結(六):3.中斷半部tasklet

linux裝置驅動歸納總結(六):3.中斷的上半部和下半部——tasklet xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 一、什麼是下半部 中斷是一個很霸道的東西,處理

字串處理sizeofstrlen區別以及末尾的\0

char *ch = "wonima aisaoziaaa"; int n = sizeof(ch); // 指標長度,對於64平臺來說,值為8 int nn = sizeof(*ch); // 一個字元的長度,值為1 int nnn = strlen(ch); //

taskletworkqueue區別

轉自 http://blog.csdn.net/av_geek/article/details/41278801 一、中斷處理的tasklet(小任務)機制 中斷服務程式一般都是在中斷請求關閉的條件下執行的,以避免巢狀而使中斷控制複雜化。但是,中斷是一個隨機事件,它

Aspectjcallexecution區別織入程式碼位置不同

call 呼叫通知的環境是方法呼叫位置;而execution是方法執行位置,也就是方法簽名處。 具體結合一個示例可以更簡單跟容易理解: 1、pointcut配置: pointcut callPointCut():execution(public

《Linux內核設計實現》讀書筆記(八)- 中斷半部的處理

sym dmesg 重新編譯 warn dad style lsp 之前 res 在前一章也提到過,之所以中斷會分成上下兩部分,是由於中斷對時限的要求非常高,需要盡快的響應硬件。 主要內容: 中斷下半部處理 實現中斷下半部的機制 總結中斷下半部的實現 中斷實現

外部中斷和內部中斷中斷 中斷 中斷半部

重要宣告: 以下程式碼有貼上 擷取他人勞動成果的成分 如有雷同 不勝榮幸 如您不能容忍 請不要獨自忍受@weChat:iisssssssssii 聯絡小民 主動刪除     中斷含義:  { 中斷是CPU處理外部突發事件的一個重要技術。它能使CPU在執行過程中對外部事件發

中斷半部-中斷

1 軟中斷概述 軟中斷是實現中斷下半部的一種手段,與2.5以前版本的BH機制不同。軟中斷可以同時執行在不同的CPU上。 1.1 軟中斷的表示 核心中用結構體softirq_action表示一個軟中斷。軟中斷是一組靜態定義的介面,有32個。但是核心(2.

中斷半部的妙用

中斷下半部 中斷處理程式是核心中很重要的一部分,但是由於本身存在著一些限制,所以他只能完成整個中斷處理程式中的上半部分。 具體侷限如下: 中斷處理程式是以非同步方式執行,並且他有可能會打斷其他重要的程式碼(甚至有可能是其他的中斷程式)的執行,因此為了避免被打斷的程式碼停止時間過

windowsvirtualboxUbuntu主機通過ssh建立連線的方法

    最近在學習Linux系統下程式設計的內容,在搭建環境的時候有一個部分是用ssh工具實現主機和虛擬機器之間互動,中間遇到了一些問題,通過在網上查詢資料發現有不少是針對vmware的,而我使用的是

中斷半部_工作佇列(work queue)

1>work_queue:<linux/workqueue.h> __3.0.4 2>description: 中斷下半部,在核心執行緒的程序上下文中執行推後的工作. 它是唯一能在程序上下文執行的中斷下半部實現機制,也只有它才可以睡眠. 3>建

10. 中斷半部---工作佇列

————————————————– 方法一 四部走 0. strunt work_queue{ //定義工作佇列物件 struct workqueue_struct *mywq; s

Linux2.6核心--中斷半部實現方法 工作佇列

      工作佇列子系統是一個用於建立核心執行緒的介面,通過它建立的程序負責執行由核心其他部分排到佇列裡的任務。它建立的這些核心執行緒稱作工作者執行緒。工作佇列可以讓你的驅動程式建立一個專門的工作者執

主要的中斷半部及其區別

中斷下半部主要有:軟中斷,tasklet,工作佇列 區別 1. 軟中斷和tasket是不可以睡眠的,任務佇列在程序上下問執行是可以睡眠的。 2.相同的軟中斷可以在不同的cpu上同時執行,型別相同的tasklet不可以同時執行,不同型別的軟中斷可以在不同的cpu同時執行

(六)3中斷半部之工作佇列

1、工作佇列的使用 按慣例,在介紹工作佇列如何實現之前,先說說如何使用工作佇列實現下半部。 步驟一、定義並初始化工作佇列: 建立工作佇列函式: struct workqueue_struct *create_workqueue(const char *nam

linux 觸控式螢幕驅動中斷半部實現-工作佇列

工作佇列(work queue)是Linux kernel中將工作推後執行的一種機制。這種機制和BH或Tasklets不同之處在於工作佇列是把推後的工作交由一個核心執行緒去執行,因此工作佇列的優勢就在於它允許重新排程甚至睡眠。工作佇列可以把工作推後,交由一個核心執行緒去執

C#outref區別

erro 變量 但是 color 賦值 運行 網上 ann amp 一、ref(參考)與out區別 1、out(只出不進) 將方法中的參數傳遞出去,在方法中將該參數傳遞出去之前需要在該方法起始賦初值;在方法外傳遞的該參數可以不用賦值; 簡單理解就是:將一個東西拋出去之前必須

iOSxibstoryboard原理Android界面布局的異同

ast int mvc color ron rect sdn -m control 用文本標記語言來進行布局,用的最多的應該是HTML語言。HTML能夠理解為有一組特殊標記的XML語言。 一、iOS中xib與storyboard顯示原理 在iOS中基

jQueryposition()offset()區別

元素 page mini alert span 文檔 back www. class jQuery中position()與offset()區別 position()獲取相對於它最近的具有相對位置(position:relative或position:absolute)

dos命令rem ::的區別

dos命令 一行 .html 等等 nbsp article 字符 log 註釋 參考: http://www.cnblogs.com/followyourdream/articles/3445862.html rem *** 是一條命令, 占一行 : **