1. 程式人生 > >Linux kernel多執行緒的幾種實現

Linux kernel多執行緒的幾種實現

驅動開發中常常會啟動幾個核心執行緒,在整個驅動生命週期期間執行某些操作,比如USB驅動的控制執行緒,一直等待SCSI命令,沒有命令的話睡眠,有命令的話就喚醒執行緒,解析執行相關的命令。還有USB驅動中的掃描執行緒,如果有新的裝置連線到USB匯流排,則會啟動掃描過程,平時時候讓出CPU資源休眠。

常用的核心執行緒建立方法有3個,kernel_thread, kthread_createkthread_run。使用這些函式或巨集需要包括如下標頭檔案:

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

#include <linux/kthread.h>//kthread_ceate(), kthread_run()

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

這些方法建立的核心執行緒必須能夠自己放棄CPU資源,即不要產生死迴圈而不主動呼叫scheduel()函式,否則這樣CPU會一直忙,因為核心程序/執行緒是不可搶佔的,所以他必須自己能夠主動的放棄資源,不管通過什麼方式。

(1)kernel_thread

原型如下

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)

該函式的實現與具體的CPU架構有關,各引數的含義如下:

fn:函式指標,指向要建立的核心執行緒的執行函式

argfn函式的引數,可以沒有,沒有寫NULL

flags:標誌,與建立他的程序共享哪些東西

該函式返回整型數,如果返回的值小於0則表示核心執行緒建立失敗,否則建立成功。

由該函式建立的程序不需要在模組清除時登出,可能執行過就自動銷燬了。

例子:

static DECLARE_WAIT_QUEUE_HEAD(my_waitqueue);

static int example_kernel_thread(void)

{

       DECLARE_WAITQUEUE(wait,current);

       daemonize(“new_kthread”);

       allow_signal(SIGKILL);

       add_wait_queue(&my_waitqueue,&wait);

       while(1)

       {

              set_current_state(TASK_INTERRUPTIBLE);

              schedule();

              if(signal_pending(current))

                     break;

}

set_current_state(TASK_RUNNING);

remove_wait_queue(&my_waitqueue,&wait);

printk(KERN_NOTICE “new thread running\n”);

return 0;

}

static __init int init_hello_world(void)

{

       int status;

       status = kernel_thread(example_kernel_thread, NULL, CLONE_FS)

       if(status < 0)

              printk(KERN_NOTICE “kernel thread create failed\n”);

       else

              printk(KERN_NOTICE “kernel thread create success\n”);

return 0;

}

static __exit void exit_hello_world(void)

{

       printk(KERN_NOTICE “exit module\n”);

       return;

}

module_init(init_hello_world);

module_exit(exit_hello_world);

(2)kthread_create

原型如下:

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

                               void *data,

                               const char namefmt[], ...)

       __attribute__((format(printf, 3, 4)));

該函式返回建立的核心執行緒的程序描述符。

各引數含義:

threadfn:函式指標,指向核心執行緒所執行的函式

data:不定型別的指標,指向核心執行緒所需要的引數

namefmt:核心執行緒名字

:類似於printf引數

例如核心執行緒的名字帶有不確定數字,可以像printf函式一樣將數字寫進核心執行緒名字。

這個函式建立的核心執行緒不能立即執行,需要呼叫wake_up_process()函式來使執行緒執行,為此定義了巨集kthread_run,他是kthread_create()函式和wake_up_process()的封裝。

例子:

static struct task_struct *test_task;

static __init int test_init_module(void)

{

       int err;

       int num = 1;

       test_task = kthread_create(test_thread,NULL,”test_task-%d”, num);

       if(IS_ERR(test_task))

{

       printk(KERN_NOTICE “create new kernel thread failed\n”);

       err = PTR_ERR(test_task);

       test_task = NULL;

       return err;

}

wake_up_process(test_task);

return 0;

}

模組退出時,需要結束所建立執行緒的執行,使用下面的函式:

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

注意:在呼叫kthread_stop函式時,執行緒函式不能已經執行結束。否則,kthread_stop函式會一直進行等待。

為了避免這種情況,需要確保執行緒沒有退出,其方法如程式碼中所示:

thread_func()

{

    // do your work here

    // wait to exit

    while(!thread_should_stop())

    {

           wait();

    }

}

exit_code()

{

     kthread_stop(_task);   //發訊號給task,通知其可以退出了

}

這種退出機制很溫和,一切盡在thread_func()的掌控之中,執行緒在退出時可以從容地釋放資源,而不是莫名其妙地被人“暗殺”。

例子程式碼:

static void test_cleanup_module(void)

{

       if(test_task)

{

       kthread_stop(test_task);

       test_task = NULL;

}

}

module_init(test_init_module);

module_exit(test_cleanup_module);

關於建立的新執行緒的執行緒函式,該函式是要來完成所需要的業務邏輯工作的,其一般框架如下:

int threadfunc(void *data)

{

       ……..

       while(1)

{

       set_current_state(TASK_UNINTERRUPTIBLE);

       //wait_event_interruptible(wq,condition),或wait_for-completion(c)也可以

       if(kthread_should_stop())

              break;

       if(condition)//

       {

              ………//業務處理

}

else//

{

       //讓出CPU,並在指定的時間內重新排程

       schedule_timeout(HZ);//延時1秒,重新排程執行

}

}

}

另外,有該函式建立的核心執行緒可以指定在不同的CPU核上執行(如果是多核的話),這個可以通過下面的函式實現:

void kthread_bind(struct task_struct *k, unsigned int cpu);

k:建立的核心執行緒的程序描述符

cpuCPU編號

(3)kthread_run

這個不是函式,是一個巨集:

/**

 * kthread_run - create and wake a thread.

 * @threadfn: the function to run until signal_pending(current).

 * @data: data ptr for @threadfn.

 * @namefmt: printf-style name for the thread.

 *

 * Description: Convenient wrapper for kthread_create() followed by

 * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM).

 */

#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;                                                     \

})

這個巨集完全同kthread_create(),只是建立完了執行緒,執行緒能夠馬上執行。

對於(2)(3)需要注意:

執行緒一旦啟動起來後,會一直執行,除非該執行緒主動呼叫do_exit函式,或者其他的程序呼叫kthread_stop函式,結束執行緒的執行。 
    int kthread_stop(struct task_struct *thread);
kthread_stop() 
通過傳送訊號給執行緒。如果執行緒函式正在處理一個非常重要的任務,它不會被中斷的。當然如果執行緒函式永遠不返回並且不檢查訊號,它將永遠都不會停止。

轉自:http://dashan8020.blog.163.com/blog/static/4796750420115180227132/