Search Google

Tuesday, June 17, 2008

[Process Management] Process/Thread Creation Part 1

專門用語:

  • 使用打字機(Courier New)字體表示原始碼。
  • task/process即為Linux中的thread。
  • 文中的Linux與Linux kernel指的皆為作業系統核心。
  • 使用斜體字型標示Linux中的資料結構,變數或函式名稱。
  • PID即為TID。

Kernel版本:
  • v2.6.24.3

今天要研究的課題是作業系統中每個在跑的process是怎麼來的?因為process可分為kernel thread與user thread,所以接下來會分兩個部分討論產生kernel thread(Part 1)與user thread(Part 2)的流程,最後比較這兩個流程之間的同異之處與補充說明(Part 3)。

Kernel thread:
建立kernel thread需要呼叫的kernel function為kernel_thread。呼叫kernel_thread時需要傳入三個變數,分別為指到執行函式的指標,指到執行函式所需參數的指標,以及新thread的屬性。於kernel_thread執行完畢時會回傳新thread的識別號碼(PID),此時新產生的thread還沒有開始運行而是被放在工作佇列(runqueue)中直到kernel呼叫schedule1

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;

memset(&regs, 0, sizeof(regs));

regs.ebx = (unsigned long) fn;
regs.edx = (unsigned long) arg;

regs.xds = __USER_DS;
regs.xes = __USER_DS;
regs.xfs = __KERNEL_PERCPU;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper;
regs.xcs = __KERNEL_CS | get_kernel_rpl();
regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;

/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}

從上面這段程式碼可以看到kernel_thread做了兩件事:先為新thread準備工作環境2再以使用者傳入的參數為依據產生新的thread,Linux使用do_fork函式產生thread。do_fork函式間接透過copy_process函式呼叫dup_task_struct函式為新thread規劃堆疊空間並建立task_structtask_thread_info。若是沒有發生任何錯誤kernel_thread函式會透過do_fork函式將新thread的PID回傳給使用者3,要特別注意的是到目前為止沒有任何一個動作讓新thread開始運作,do_fork函式最多4只呼叫wake_up_new_task函式將新thread放入runqueue中等待scheduler將其挑出執行,因此新thread此時只不過是runqueue中的一個等待執行的task。

除了kernel_thread函式之外kthread_create函式也可以建立新thread,不同的是kthread_create會延遲新thread的產生,它將有關新thread的資訊放入kthread_create_list然後讓kthreadd_task5分別為kthread_create_list中所存放的各個資訊建立相對應的新thread,而kthreadd_task最後還是透過create_kthread函式使用kernel_thread函式建立新thread。



1 呼叫schedule僅代表任何thread都有可能被挑出來執行,並不表示新產生的thread一定會馬上被挑到。
2 這裡所準備的registers就是讓一個thread正常運作的最基本資訊,也是之前討論過的task_struct基本要素。
3 使用者此時編輯的是kernel mode程式碼,包含kernel的功能或module。
4 建立新thread時可以使用CLONE_STOPPED參數讓新thread不要馬上進入runqueue。
5 kthreadd_taskkernel_init為Linux開機最末段所產生的兩支kernel threads,kthreadd_task是用來非同步產生thread(多的那個"d"應該代表著daemon的意思,不是typo喔!),而kernel_init則是從開機程序切入使用者介面(eg. bash)的關鍵。

No comments: