Linux 核心 tasklet 機制和工作佇列

發表於2016-10-27

1. Tasklet機制分析

上面我們介紹了軟中斷機制,linux核心為什麼還要引入tasklet機制呢?主要原因是軟中斷的pending標誌位也就32位,一般情況是不隨意增加軟中斷處理的。而且核心也沒有提供通用的增加軟中斷的介面。其次內,軟中斷處理函式要求可重入,需要考慮到競爭條件比較多,要求比較高的程式設計技巧。所以核心提供了tasklet這樣的一種通用的機制。

其實每次寫總結的文章,總是想把細節的東西說明白,所以越寫越多。這樣做的好處是能真正理解其中的機制。但是,內容太多的一個壞處就是難道記憶,所以,在講清楚講詳細的同時,我還要把精髓總結出來。Tasklet的特點,也是tasklet的精髓就是:tasklet不能休眠,同一個tasklet不能在兩個CPU上同時執行,但是不同tasklet可能在不同CPU上同時執行,則需要注意共享資料的保護。

主要的資料結構

static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);

static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

如何使用tasklet

使用tasklet比較簡單,只需要初始化一個tasklet_struct結構體,然後呼叫tasklet_schedule,就能利用tasklet機制執行初始化的func函式。

tasklet_schedule處理過程也比較簡單,就是把tasklet_struct結構體掛到tasklet_vec連結串列或者掛接到tasklet_hi_vec連結串列上,並排程軟中斷TASKLET_SOFTIRQ或者HI_SOFTIRQ

Tasklet執行過程

Tasklet_action在軟中斷TASKLET_SOFTIRQ被排程到後會被執行,它從tasklet_vec連結串列中把tasklet_struct結構體都取下來,然後逐個執行。如果t->count的值等於0,說明這個tasklet在排程之後,被disable掉了,所以會將tasklet結構體重新放回到tasklet_vec連結串列,並重新排程TASKLET_SOFTIRQ軟中斷,在之後enable這個tasklet之後重新再執行它。

2. Linux工作佇列

前面已經介紹了tasklet機制,有了tasklet機制為什麼還要增加工作佇列機制呢?我的理解是由於tasklet機制的限制,變形tasklet中的回撥函式有很多的限制,比如不能有休眠的操作等等。而是用工作佇列機制,需要處理的函式在程式上下文中呼叫,休眠操作都是允許的。但是工作佇列的實時性不如tasklet,採用工作佇列的例程可能不能在短時間內被呼叫執行。

資料結構說明

首先需要說明的是workqueue_struct和cpu_workqueue_struct這兩個資料結構,建立一個工作佇列首先需要建立workqueue_struct,然後可以在每個CPU上建立一個cpu_workqueue_struct管理結構體。

Work_struct表示將要提交的處理的工作。

上面三個資料結構的關係如下圖所示

wps_clip_image-32340

介紹主要資料結構的目的並不是想要把工作佇列具體的細節說明白,主要的目的是給大家一個總的架構的輪廓。具體的分析在下面展開。從上面的該模組主要資料結構的關係來看,主要需要分析如下幾個問題:

1. Workqueque是怎樣建立的,包括event/0核心程式的建立

2. Work_queue是如何提交到工作佇列的

3. Event/0核心程式如何處理提交到佇列上的工作

Workqueque的建立

首先申請了workqueue_struct結構體記憶體,cpu_workqueue_struct結構體的記憶體。然後在init_cpu_workqueue函式中對cpu_workqueue_struct結構體進行初始化。同時呼叫create_workqueue_thread函式建立處理工作佇列的核心程式。

create_workqueue_thread中建立瞭如下的核心程式

p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);

最後呼叫start_workqueue_thread啟動新建立的程式。

向工作佇列中新增工作

Shedule_work 函式向工作佇列中新增任務。這個介面比較簡單,無非是一些佇列操作,不再敘述。

工作佇列核心程式的處理過程

在建立工作佇列的時候,我們建立了一個或者多個程式來處理掛到佇列上的工作。這個核心程式的主要函式體為worker_thread,這個函式比較有意思的地方就是,自己降低的優先順序,說明worker_thread排程的優先順序比較低。在系統負載大大時候,採用工作佇列執行的操作可能存在較大的延遲。

就函式的執行流程來說是真心的簡單,只是從佇列中取出work,從佇列中刪除掉,清除掉pending標記,並執行work設定的回撥函式。

相關文章