第六週分析Linux內核建立一個新進程的過程

潘恆 原創做品轉載請註明出處《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000node

task_struct結構:linux

struct task_struct { 
 volatile long state;進程狀態
 void *stack; 堆棧
 
 
 pid_t pid; 進程標識符
 unsigned int rt_priority;實時優先級
 unsigned int policy;調度策略
 struct files_struct *files;系統打開文件
 ...
}
 
 
內核處理函數sys_clone:
    系統調用經過do_fork實現進程的建立:
return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
 
 
linux系統是經過複製父進程的信息來建立子進程,在do_fork函數中,真正實現複製的是copy_process函數:


p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace);
 
 
p = dup_task_struct(current);建立內核棧
retval = security_task_create(clone_flags);
retval = sched_fork(clone_flags, p);和調度相關的設置,cpu將調度這個task
retval = copy_thread(clone_flags, stack_start, stack_size, p); 複製父進程堆棧的內容到子進程的堆棧中去.這其中,copy_thread函數中的語句p->thread.ip = (unsigned long) ret_from_fork決定了新進程的第一條指令地址.

建立棧函數dup_task_struct:
tsk = alloc_task_struct_node(node);開闢內存空間
ti = alloc_thread_info_node(tsk, node);ti指向thread_info的首地址,同時也是系統爲新進程分配的兩個連續頁面的首地址。
err = arch_dup_task_struct(tsk, orig);複製父進程的task_struct信息到新的task_struct裏
tsk->stack = ti;task對應棧
setup_thread_stack(tsk, orig);初始化thread info結構
set_task_stack_end_magic(tsk);棧結束的地址設置數據爲棧結束標示


新進程從哪裏開始執行:

在以前的分析中,談到copy_process中的copy_thread()函數,正是這個函數決定了子進程從系統調用中返回後的執行.ret_from_fork決定了新進程的第一條指令地址。p->thread.ip = (unsigned long) ret_from_fork;將子進程的ip設置爲ret_from_fork的首地址,子進程從ret_from_fork開始執行
 
 

執行起點與內核堆棧如何保持一致

1. 在ret_from_fork以前,也就是在copy_thread()函數中*childregs = *current_pt_regs();該句將父進程的regs參數賦值到子進程的內核堆棧,

2. *childregs的類型爲pt_regs,裏面存放了SAVE ALL中壓入棧的參數

3. 故在以後的RESTORE ALL中能順利執行下去.

 

實驗:分析Linux內核建立一個新進程的過程c#

 

 

 

總結:

1.Linux經過複製父進程來建立一個新進程,經過調用do_fork來實現。函數

2.Linux爲每一個新建立的進程動態地分配一個task_struct結構。線程

3.爲了把內核中的全部進程組織起來,Linux提供了幾種組織方式,其中哈希表和雙向循環鏈表方式是針對系統中的全部進程(包括內核線程),而運行隊列和等待隊列是把處於同一狀態的進程組織起來。code

4.fork()函數被調用一次,但返回兩次。blog

相關文章
相關標籤/搜索