1. 程式人生 > 其它 >Linux核心執行緒

Linux核心執行緒

1.核心執行緒

Linux核心可以看作一個服務程序(管理軟硬體資源,響應使用者程序的種種合理以及不合理的請求)。核心需要多個執行流並行,為了防止可能的阻塞,多執行緒化是必要的。

核心執行緒就是核心的分身,一個分身可以處理一件特定事情。Linux核心使用核心執行緒來將核心分成幾個功能模組,像kswapd、kflushd等,這在處理非同步事件如非同步IO時特別有用。核心執行緒的使用是廉價的,唯一使用的資源就是核心棧和上下文切換時儲存暫存器的空間。支援多執行緒的核心叫做多執行緒核心(Multi-Threads kernel )。

核心執行緒的排程由核心負責,一個核心執行緒處於阻塞狀態時不影響其他的核心執行緒,因為其是排程的基本單位。這與使用者執行緒是不一樣的。

核心執行緒只執行在核心態,不受使用者態上下文的拖累。

  • 處理器競爭:可以在全系統範圍內競爭處理器資源;
  • 使用資源:唯一使用的資源是核心棧和上下文切換時保持暫存器的空間
  • 排程:排程的開銷可能和程序自身差不多昂貴
  • 同步效率:資源的同步和資料共享比整個程序的資料同步和共享要低一些。

2.核心執行緒與普通程序的異同

1.跟普通程序一樣,核心執行緒也有優先順序和被排程。 當和使用者程序擁有相同的static_prio時,核心執行緒有機會得到更多的cpu資源

2.核心執行緒的bug直接影響核心,很容易搞死整個系統, 但是使用者程序處在核心的管理下,其bug最嚴重的情況也只會把自己整崩潰

3.核心執行緒沒有自己的地址空間

,所以它們的”current->mm”都是空的;

4.核心執行緒只能在核心空間操作,不能與使用者空間互動;

核心執行緒不需要訪問使用者空間記憶體,這是再好不過了。所以核心執行緒的task_struct的mm域為空.但是剛才說過,核心執行緒還有核心堆疊,沒有mm怎麼訪問它的核心堆疊呢?這個核心堆疊跟task_struct的thread_info共享8k的空間,所以不用mm描述。

但是核心執行緒總要訪問核心空間的其他核心啊,沒有mm域畢竟是不行的。所以核心執行緒被呼叫時, 核心會將其task_strcut的active_mm指向前一個被排程出的程序的mm域, 在需要的時候,核心執行緒可以使用前一個程序的記憶體描述符。

因為核心執行緒不訪問使用者空間,只操作核心空間記憶體,而所有程序的核心空間都是一樣的。這樣就省下了一個mm域的記憶體。

3. 核心執行緒建立

在核心中,有兩種方法可以生成核心執行緒,一種是使用kernel_thread()介面,另一種是用kthread_create()介面

3.1 kernel_thread

先說kernel_thread介面,使用該介面建立的執行緒,必須在該執行緒中呼叫daemonize()函式,這是因為只有當執行緒的父程序指向”Kthreadd”時,該執行緒才算是核心執行緒,而恰好daemonize()函式主要工作便是將該執行緒的父程序改成“kthreadd”核心執行緒;預設情況下,呼叫deamonize()後,會阻塞所有訊號,如果想操作某個訊號可以呼叫allow_signal()函式

int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); 
            // fn為執行緒函式,arg為執行緒函式引數,flags為標記
void daemonize(const char * name,...); // name為核心執行緒的名稱

3.2 kthread_create

而kthread_create介面,則是標準的核心執行緒建立介面,只須呼叫該介面便可建立核心執行緒;預設建立的執行緒是存於不可執行的狀態,所以需要在父程序中通過呼叫wake_up_process()函式來啟動該執行緒。

struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,
                                  const char namefmt[], ...);
 //threadfn為執行緒函式;data為執行緒函式引數;namefmt為執行緒名稱,可被格式化的, 類似printk一樣傳入某種格式的執行緒名

執行緒建立後,不會馬上執行,而是需要將kthread_create() 返回的task_struct指標傳給wake_up_process(),然後通過此函式執行執行緒。

3.3 kthread_run

當然,還有一個建立並啟動執行緒的函式:kthread_run

struct task_struct *kthread_run(int (*threadfn)(void *data),
                                    void *data,
                                    const char *namefmt, ...);

執行緒一旦啟動起來後,會一直執行,除非該執行緒主動呼叫do_exit函式,或者其他的程序呼叫kthread_stop函式,結束執行緒的執行。

int kthread_stop(struct task_struct *thread);

kthread_stop() 通過傳送訊號給執行緒。
如果執行緒函式正在處理一個非常重要的任務,它不會被中斷的。當然如果執行緒函式永遠不返回並且不檢查訊號,它將永遠都不會停止。

int wake_up_process(struct task_struct *p); //喚醒執行緒
struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,
                                const char namefmt[], ...);//是以上兩個函式的功能的總和

因為執行緒也是程序,所以其結構體也是使用程序的結構體”struct task_struct”。

核心執行緒的退出當執行緒執行到函式末尾時會自動呼叫核心中do_exit()函式來退出或其他執行緒呼叫kthread_stop()來指定執行緒退出。