kthread run建立核心執行緒的原理

2021-08-04 04:09:47 字數 3545 閱讀 4079

kthread_run是乙個巨集,用來建立乙個程序,並且將其喚醒,其定義在標頭檔案include/linux/kthread.h中.

#define kthread_run(threadfn, data, namefmt, ...)               \

()先來看看這個巨集的引數,threadfn是該執行緒的執行函式,data是執行函式的傳入引數,namefmt是該執行緒的printf風格的執行緒名,最後的...類似printf的可變引數列表.從這個巨集的實現可以看出kthread_run是通過kthread_create來建立乙個程序,並返回乙個task_struct,然後使用wake_up_process函式將新建立的程序喚醒.下面需要關注一下kthread_create的實現,其實它也是乙個巨集,同樣也定義在標頭檔案include/linux/kthread.h中.

#define kthread_create(threadfn, data, namefmt, arg...) \

kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)

該巨集的引數同kthread_run,不解釋了,其實現就是呼叫了函式kthread_create_on_node,下面來看該函式的實現.

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

void *data, int node,

const char namefmt,

...)

task = create->result;

if (!is_err(task)) ;

va_list args;

va_start(args, namefmt);

vsnprintf(task->comm, sizeof(task->comm), namefmt, args);//設定執行緒名字

va_end(args);

sched_setscheduler_nocheck(task, sched_normal, ¶m);

set_cpus_allowed_ptr(task, cpu_all_mask);

}kfree(create);

return task;

}該函式首先建立乙個kthread_create_info結構,該結構封裝了要建立的核心程序所需要的資訊,然後將該結構新增到鍊錶kthread_create_list的末尾,接著該函式呼叫wake_up_process函式喚醒kthreadd_task所表示的核心執行緒,然後呼叫wait_for_completion系列的函式使當前程序進入睡眠狀態,在這裡我們可以猜想kthreadd_task表示的核心執行緒應該會從鍊錶kthread_create_list中依次的取下每個節點根據對應的kthread_create_info結構中的執行緒資訊來建立核心執行緒,然後通過completion函式喚醒當前執行緒,這時kthread_create_info中的result應該已經被賦值為剛建立的核心執行緒的task_struct結構了,kthread_create_on_node函式會檢查該task_struct結構,判斷核心執行緒建立是否成功,如果成功,會進一步設定執行緒task的名稱,以及排程器,最後返回新建立的task_struct結構體.

剛才猜想了kthreadd_task表示的核心執行緒的作用是從鍊錶kthread_create_list上依次的取下kthread_create_info結構,然後建立相應的核心執行緒.現在來驗證這個猜想.在核心原始碼中搜尋kthreadd_task,會在init/main.c檔案中找到為kthreadd_task賦值的地方.在函式rest_init中.

static noinline void __init_refok rest_init(void)

rest_init算是kernel再啟動函式start_kernel中執行的最後乙個函式,kthreadd_task表示的核心執行緒就是在這裡建立的,這行**kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns)是通過pid來查詢其對應的task_struct的結構體,再看pid是上面的這行**產生的pid = kernel_thread(kthreadd, null, clone_fs | clone_files),kernel_thread函式是通過do_fork來建立執行緒的,然後返回該執行緒的pid,從這裡可以看出kthreadd_task表示的核心執行緒的執行函式是kthreadd,下面來看這個函式的實現.

int kthreadd(void *unused)

spin_unlock(&kthread_create_lock);

}return 0;

}可以看出這個函式的主體是乙個for迴圈,重點是其中的那個while迴圈,很明顯是在遍歷鍊錶kthread_create_list,獲取將其上面的每個節點所表示的kthread_create_info結構,然後將該結構傳遞給函式create_kthread,顧名思義,該函式就是來建立執行緒的,下面看看create_kthread的**.

static void create_kthread(struct kthread_create_info *create)

create->result = err_ptr(pid);

complete(done);}}

該函式呼叫了kernel_thread函式建立了乙個以kthread函式為執行緒執行函式,以kthread_create_info結構為執行函式引數的核心執行緒,其實這個新建立的執行緒就是最終需要建立的那個核心執行緒,那為什麼執行函式是kthread,而不是kthread_create_info結構中指定的執行緒執行函式.看完kthread函式的實現就知道了.

static int kthread(void *_create)

__set_current_state(task_uninterruptible);

create->result = current;//將該執行緒的task_struct結構賦值給create->result

complete(done);//呼叫complete函式喚醒呼叫kthread_create_on_node的那個執行緒,通知它建立的執行緒已經建立完畢.

schedule();//呼叫schedule交出處理器,給剛喚醒的呼叫kthread_create_on_node的執行緒以執行的機會,使其繼續執行kthread_create_on_node函式剩下的**.

//再次排程到該執行緒,從這裡開始執行.

ret = -eintr;

if (!test_bit(kthread_should_stop, &self.flags))

do_exit(ret);

}可以看出,如果在kthread函式呼叫schedule交出處理器和再次排程到該執行緒之間的這段時間裡,在別的地方呼叫了kthread_stop函式停止了該函式的話,那麼執行緒執行函式是得不到執行的,該執行緒就直接退出了.

以上就是通過kthread_run和kthread_create建立執行緒的整個過程.可以看出,它們並不直接建立執行緒,而是將要建立的執行緒的相關資訊打包到kthread_create_info結構中,然後委託給核心執行緒kthreadd來做的,從函式kthreadd也可以看出,該執行緒的主要工作就是遍歷鍊錶kthread_create_list建立執行緒.

建立核心執行緒

在作業系統的最小執行單元就是執行緒,在核心中線程的概念更加容易看出來。比如說有的時候需要使用執行緒來完成一些任務,可是這些任務的工作量過大的時候系統處理這些任務就必須停下來等待。而等待的過程就大大的浪費了cup寶貴的時間,所以這個時候利用多執行緒去處理是最好的方法。在驅動裡生成的執行緒一般是系統執行...

建立核心執行緒

在作業系統的最小執行單元就是執行緒,在核心中線程的概念更加容易看出來。比如說有的時候需要使用執行緒來完成一些任務,可是這些任務的工作量過大的時候系統處理這些任務就必須停下來等待。而等待的過程就大大的浪費了cup寶貴的時間,所以這個時候利用多執行緒去處理是最好的方法。在驅動裡生成的執行緒一般是系統執行...

建立核心執行緒

在linux中,有一些程序完全執行在核心空間,比如ksoftirqd等等,這些程序稱為核心執行緒。今天,我們就動手建立乙個核心執行緒。引言 如果使用者層的程序違規訪問記憶體,核心會傳送乙個sigse 訊號給程序。我們的目標就是,建立乙個核心執行緒,如果發生了段錯誤,就在控制台列印 mykthread...