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;

}


免責聲明!

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



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