執行不息的核心執行緒kthread

2021-07-04 19:35:23 字數 3211 閱讀 2938

上節中,我們成功地編譯執行了乙個linux模組。可惜的是,它只有兩個函式,hello_init在模組載入時呼叫,hello_exit 在模組解除安裝時呼叫。這樣下去,模組縱使有天大的本事,也只能壓縮在這兩個函式中。為了避免這種悲劇發生,本節就來學習一種讓模組在載入後能一直執行下去的方法——核心執行緒。

要建立乙個核心執行緒有許多種方法,我們這裡要學的是最簡單的一種。開啟include/linux/kthread.h,你就看到了它全部的api,一共三個函式。

[cpp]view plain

copy

struct

task_struct kthread_run(

int(*threadfn)(

void

*data),  

void

*data, 

const

char

namefmt,...);  

intkthread_stop(

struct

task_struct *k);  

intkthread_should_stop(

void

);  

kthread_run()負責核心執行緒的建立,引數包括入口函式threadfn,引數data,執行緒名稱namefmt。可以看到執行緒的名字可以是類似sprintf方式組成的字串。如果實際看到kthread.h檔案,就會發現kthread_run實際是乙個巨集定義,它由kthread_create()和wake_up_process()兩部分組成,這樣的好處是用kthread_run()建立的執行緒可以直接執行,使用方便。

kthread_stop()負責結束建立的執行緒,引數是建立時返回的task_struct指標。kthread設定標誌should_stop,並等待執行緒主動結束,返回執行緒的返回值。執行緒可能在kthread_stop()呼叫前就結束。(經過實際驗證,如果執行緒在kthread_stop()呼叫之前就結束,之後kthread_stop()再呼叫會發生可怕地事情—呼叫kthread_stop()的程序crash!!之所以如此,是由於kthread實現上的弊端,之後會專門寫文章講到)

kthread_should_stop()返回should_stop標誌。它用於建立的執行緒檢查結束標誌,並決定是否退出。執行緒完全可以在完成自己的工作後主動結束,不需等待should_stop標誌。

下面就來嘗試一下執行不息的核心執行緒吧。

1、把上節建立的hello子目錄,複製為新的kthread子目錄。

2、修改hello.c,使其內容如下。

[cpp]view plain

copy

#include 

#include 

#include 

module_license("dual bsd/gpl"

);  

static

struct

task_struct *tsk;  

static

intthread_function(

void

*data)  

while

(!kthread_should_stop() && time_count<=30);  

return

time_count;  

}  static

inthello_init(

void

)    

else

return

0;  

}  static

void

hello_exit(

void

)    

}  module_init(hello_init);  

module_exit(hello_exit);  

為了不讓建立的核心執行緒一直執行浪費cpu,**中採用週期性延遲的方式,每次迴圈用msleep(1000)延遲1s。為了防止執行緒一直執行下去,**中使用了兩個結束條件:乙個是模組要求執行緒結束,乙個是列印滿一定次數,後者是為了防止printk輸出資訊太多。最後在hello_exit中結束執行緒,並列印執行緒執行的時間。

這裡要注意的是kthread_run的返回值tsk。不能用tsk是否為null進行檢查,而要用is_err()巨集定義檢查,這是因為返回的是錯誤碼,大致從0xfffff000~0xffffffff。

3、編譯執行模組,步驟參照前例。在執行過程中使用ps -e命令,可以看到有名字位mythread1的核心執行緒在執行。

經過本節,我們學習了核心執行緒的建立使用方法,現在要建立一大堆的執行緒在核心中已經易如反掌。你會逐漸相信,我們模組的拓展空間是無限的。

附註:

我們的重點在模組程式設計,不斷學習核心api的使用。但如果能知其然,而知其所以然就更好了。所以有了文章後的附註部分。在附註部分,我們會盡量解釋核心api的實現原理,對相關linux核心**做簡單的分析,以幫助大家學習理解相關的**。分析的**包含在linux-2.6.32中,但這些**在相近版本中都變化不大。作者水平有限,請大家見諒。

kthread的實現在kernel/kthread.c中,標頭檔案是include/linux/kthread.h。核心中一直執行乙個執行緒kthreadd,它執行kthread.c中的kthreadd函式。在kthreadd()中,不斷檢查乙個kthread_create_list鍊錶。kthread_create_list中的每個節點都是乙個建立核心執行緒的請求,kthreadd()發現鍊錶不為空,就將其第乙個節點退出鍊錶,並呼叫create_kthread()建立相應的執行緒。create_kthread()則進一步呼叫更深層的kernel_thread()建立執行緒,入口函式設在kthread()中。

外界呼叫kthread_run建立執行執行緒。kthread_run是個巨集定義,首先呼叫kthread_create()建立執行緒,如果建立成功,再呼叫wake_up_process()喚醒新建立的執行緒。kthread_create()根據引數向kthread_create_list中傳送乙個請求,並喚醒kthreadd,之後會呼叫wait_for_completion(&create.done)等待執行緒建立完成。新建立的執行緒開始執行後,入口在kthread(),kthread()呼叫complete(&create->done)喚醒阻塞的模組程序,並使用schedule()排程出去。kthread_create()被喚醒後,設定新執行緒的名稱,並返回到kthread_run中。kthread_run呼叫wake_up_process()重新喚醒新建立執行緒,此時新執行緒才開始執行kthread_run引數中的入口函式。

執行不息的核心執行緒kthread

要建立乙個核心執行緒有許多種方法,我們這裡要學的是最簡單的一種。開啟include linux kthread.h,你就看到了它全部的api,一共三個函式。structtask struct kthread run int threadfn void data void data,const cha...

執行不息的核心執行緒kthread

要建立乙個核心執行緒有許多種方法,我們這裡要學的是最簡單的一種。開啟include linux kthread.h,你就看到了它全部的api,一共三個函式。structtask struct kthread run int threadfn void data void data,const cha...

核心執行緒是怎麼執行

pid t kernel thread int fn void void arg,unsigned long flags 通過這個函式可以建立核心執行緒,執行乙個指定函式fn。但是這個fn是怎麼執行的了?pid t kernel thread int fn void void arg,unsigne...