Linux內核多線程實現方法 —— kthread_create函數【轉】


轉自:http://blog.csdn.net/sharecode/article/details/40076951

Linux內核多線程實現方法 —— kthread_create函數

 

內核經常需要在后台執行一些操作,這種任務就可以通過內核線程(kernle thread)完成獨立運行在內核空間的標准進程。內核線程和普通的進程間的區別在於內核線程沒有獨立的地址空間,mm指針被設置為NULL;它只在內核空間運行,從來不切換到用戶空間去;並且和普通進程一樣,可以被調度,也可以被搶占。實際上,內核線程只能由其他內核線程創建,在現有的內核線程中創建一個新的內核線程的方法:

kthread_create:創建線程。
struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,const char *namefmt, ...);//注意,第二個參數data用於向線程傳遞參數

線程創建后,不會馬上運行,而是需要將kthread_create() 返回的task_struct指針傳給wake_up_process(),然后通過此函數運行線程。

kthread_run :創建並啟動線程的函數,相當於kthread_create +  wake_up_process功能;

struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt, ...);
kthread_stop:通過發送信號給線程,使之退出。
int kthread_stop(struct task_struct *thread);線程一旦啟動起來后,會一直運行,除非該線程主動調用do_exit函數,或者其他的進程調用kthread_stop函數,結束線程的運行。 但如果線程函數正在處理一個非常重要的任務,它不會被中斷的。當然如果線程函數永遠不返回並且不檢查信號,它將永遠都不會停止,因此,線程函數必須能讓出CPU,以便能運行其他線程。同時線程函數也必須能重新被調度運行。在例子程序中,這是通過schedule_timeout()函數完成的(下面的例子會看到)。

 

1.      頭文件

#include <linux/sched.h>  //wake_up_process()

#include <linux/kthread.h>//kthread_create()、kthread_run()

#include<err.h>             //IS_ERR()、PTR_ERR()

2.      實現

2.1創建線程

在模塊初始化時,可以進行線程的創建。使用下面的函數和宏定義:

struct task_struct *kthread_create(int (*threadfn)(void *data),

                           void *data,

                           const char namefmt[], ...);

#define kthread_run(threadfn, data, namefmt,...)                    \

({                                                           \

    struct task_struct*__k                                       \

          = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \

    if(!IS_ERR(__k))                                       \

          wake_up_process(__k);                               \

   __k;                                                    \

})

例如

static struct task_struct *test_task;

static inttest_init_module(void)    //驅動加載函數

{

    int err;

    test_task = kthread_create(threadfunc, NULL, "test_task");

    if(IS_ERR(test_task)){

     printk("Unable to start kernel thread.\n");

      err = PTR_ERR(test_task);

      test_task =NULL;

      return err;

    }

wake_up_process(test_task);
       return 0;
   }

   module_init(test_init_module);

2.2線程函數

在線程函數里,完成所需的業務邏輯工作。主要框架如下所示:

int threadfunc(void *data){

       …

       while(1){

              set_current_state(TASK_UNINTERRUPTIBLE);//將當前的狀態表示設置為休眠

if(kthread_should_stop()) break;  //解釋見“注意”

              if(){//條件為真

                     //進行業務處理

              }

              else{//條件為假

                     //讓出CPU運行其他線程,並在指定的時間內重新被調度

                    schedule_timeout(HZ);   // 休眠,與set_current_state配合使用,需要計算,這里表示休眠一秒

              }

       }

       …

       return 0;

}

注意:

a. 值得一提的是kthread_should_stop函數,我們需要在開啟的線程中嵌入該函數並檢查此函數的返回值,否則kthread_stop是不起作用的

b. 休眠有兩種相關的狀態:TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE。它們的惟一卻不是處於TASK_UNINTERRUPTIBLE狀態的進程會忽略信號,而處於TASK_INTERRUPTIBLE狀態的進程如果收到信號會被喚醒並處理信號(然后再次進入等待睡眠狀態)。兩種狀態的進程位於同一個等待隊列上,等待某些事件,不能夠運行。

c.schedule_time(s*HZ)的參數為節拍數,HZ宏每個系統定義不一樣,表示每一秒時鍾中斷數,如在2.6中為1000,2.4中為100, s為秒單位,例如如果要休眠20ms,則schedule_time(0.02*HZ)就可以了。

2.3結束線程

在模塊卸載時,可以結束線程的運行。使用下面的函數:

int kthread_stop(structtask_struct *k);

例如:

static void test_cleanup_module(void)

{

           if(test_task){

               kthread_stop(test_task);

               test_task = NULL;

           }

}

module_exit(test_cleanup_module);

3.       注意事項

(1)       在調用kthread_stop函數時,線程函數不能已經運行結束。否則,kthread_stop函數會一直進行等待。在執行kthread_stop的時候,目標線程必須沒有退出,否則會Oops。原因很容易理解,當目標線程退出的時候,其對應的task結構也變得無效,kthread_stop引用該無效task結構就會出錯。

(2)       線程函數必須能讓出CPU,以便能運行其他線程。同時線程函數也必須能重新被調度運行。在例子程序中,這是通過schedule_timeout()函數完成的。

4.性能測試

可以使用top命令來查看線程(包括內核線程)的CPU利用率。命令如下:

       top–p 線程號

可以使用下面命令來查找線程號:

       psaux|grep 線程名


可以用下面的命令顯示所有內核線程:
      ps afx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM