Linux kernel多執行緒的幾種實現
驅動開發中常常會啟動幾個核心執行緒,在整個驅動生命週期期間執行某些操作,比如USB驅動的控制執行緒,一直等待SCSI命令,沒有命令的話睡眠,有命令的話就喚醒執行緒,解析執行相關的命令。還有USB驅動中的掃描執行緒,如果有新的裝置連線到USB匯流排,則會啟動掃描過程,平時時候讓出CPU資源休眠。
常用的核心執行緒建立方法有3個,kernel_thread, kthread_create和kthread_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:函式指標,指向要建立的核心執行緒的執行函式
arg:fn函式的引數,可以沒有,沒有寫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:建立的核心執行緒的程序描述符
cpu:CPU編號
(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/