linux 建立核心執行緒
Linux核心可以看作一個服務程序(管理軟硬體資源,響應使用者程序的種種合理以及不合理的請求)。核心需要多個執行流並行,為了防止可能的阻塞,支援多執行緒是必要的。核心執行緒就是核心的分身,一個分身可以處理一件特定事情。核心執行緒的排程由核心負責,一個核心執行緒處於阻塞狀態時不影響其他的核心執行緒,因為其是排程的基本單位。這與使用者執行緒是不一樣的。因為核心執行緒只執行在核心態,因此,它只能使用大於PAGE_OFFSET(3G)的地址空間。核心執行緒和普通的程序間的區別在於核心執行緒沒有獨立的地址空間,mm指標被設定為NULL;它只在 核心空間執行,從來不切換到使用者空間去;並且和普通程序一樣,可以被排程,也可以被搶佔。
核心執行緒(thread)或叫守護程序(daemon),在作業系統中佔據相當大的比例,當Linux作業系統啟動以後,你可以用”ps -ef”命令檢視系統中的程序,這時會發現很多以”d”結尾的程序名,確切說名稱顯示裡面加 "[]"的,這些程序就是核心執行緒。
建立核心執行緒最基本的兩個介面函式是:
kthread_run(threadfn, data, namefmt, ...)
和
kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)
這裡我們主要介紹kthread_run,後面會專門分析這兩個函式的異同。
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_run()負責核心執行緒的建立,它由kthread_create()和wake_up_process()兩部分組成,這樣的好處是用kthread_run()建立的執行緒可以直接執行。外界呼叫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引數中的入口函式。
在介紹完如何建立執行緒之後,下面來介紹另外兩個基本的函式:
int kthread_stop(struct task_struct *k);
int kthread_should_stop(void);
kthread_stop()負責結束建立的執行緒,引數是建立時返回的task_struct指標。kthread設定標誌should_stop,並等待執行緒主動結束,返回執行緒的返回值。在呼叫 kthread_stop()結束執行緒之前一定要檢查該執行緒是否還在執行(通過 kthread_run 返回的 task_stuct 是否有效),否則會造成災難性的後果。kthread_run的返回值tsk。不能用tsk是否為NULL進行檢查,而要用IS_ERR()巨集定義檢查,這是因為返回的是錯誤碼,大致從0xfffff000~0xffffffff。
kthread_should_stop()返回should_stop標誌(參見 struct kthread )。它用於建立的執行緒檢查結束標誌,並決定是否退出。
kthread() (注:原型為:static int kthread(void *_create) )的實現在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_stop()刪除執行緒。kthread_stop首先設定結束標誌should_stop,然後呼叫wake_for_completion(&kthread->exited)上,這個其實是新執行緒task_struct上的vfork_done,會線上程結束呼叫do_exit()時設定。
附:
struct kthread { int should_stop; struct completion exited; }; int kthreadd(void *unused) { struct task_struct *tsk = current; /* Setup a clean context for our children to inherit. */ set_task_comm(tsk, "kthreadd"); ignore_signals(tsk); set_cpus_allowed_ptr(tsk, cpu_all_mask); set_mems_allowed(node_states[N_HIGH_MEMORY]); current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG; for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (list_empty(&kthread_create_list)) schedule(); __set_current_state(TASK_RUNNING); spin_lock(&kthread_create_lock); while (!list_empty(&kthread_create_list)) { struct kthread_create_info *create; create = list_entry(kthread_create_list.next, struct kthread_create_info, list); list_del_init(&create->list); spin_unlock(&kthread_create_lock); create_kthread(create); spin_lock(&kthread_create_lock); } spin_unlock(&kthread_create_lock); } return 0; } /** * kthread_stop - stop a thread created by kthread_create(). * @k: thread created by kthread_create(). * * Sets kthread_should_stop() for @k to return true, wakes it, and * waits for it to exit. This can also be called after kthread_create() * instead of calling wake_up_process(): the thread will exit without * calling threadfn(). * * If threadfn() may call do_exit() itself, the caller must ensure * task_struct can't go away. * * Returns the result of threadfn(), or %-EINTR if wake_up_process() * was never called. */ int kthread_stop(struct task_struct *k) { struct kthread *kthread; int ret; trace_sched_kthread_stop(k); get_task_struct(k); kthread = to_kthread(k); barrier(); /* it might have exited */ if (k->vfork_done != NULL) { kthread->should_stop = 1; wake_up_process(k); wait_for_completion(&kthread->exited); } ret = k->exit_code; put_task_struct(k); trace_sched_kthread_stop_ret(ret); return ret; }