進程的描述
進程控制塊PCB -- task_struct
操作系統的三大核心功能:
1、進程管理
2、內存管理
3、文件系統
關于task_struct的具體介紹,見http://blog.csdn.net/npy_lp/article/details/7292563
它定義在linux-3.18.6/include/linux/sched.h文件中。
進程(Process)是系統進行資源分配和調度的基本單位,一個進程是一個程序的運行實例。而在Linux中,可以使用一個進程來創建另外一個進程。這樣的話,Linux的進程的組織結構其實有點像Linux目錄樹,是個層次結構的,可以使用 pstree命令來查看。在最上面是init程序的執行進程。它是所有進程的老祖宗。Linux提供了兩個函數來創建進程。
1.fork()
fork()提供了創建進程的基本操作,可以說它是Linux系統多任務的基礎。該函數在/linux-3.18.6/kernel/fork.c
2.exec系列函數
如果只有fork(),肯定是不完美的,因為fork()只能參數一個父進程的副本。而exec系列函數則可以幫助我們建立一個全新的新進程。
在Linux系統中,一個進程的PCB是一個C語言的結構體task_struct來表示,而多個PCB之間是由一個雙向鏈表組織起來的,在《Understanding the Linux Kernel》中,則是進一步描述這個鏈表是一個雙向循環鏈表。
在Linux中創建一個新進程的方法是使用fork函數,fork()執行一次但有兩個返回值。
在父進程中,返回值是子進程的進程號;在子進程中,返回值為0。因此可通過返回值來判斷當前進程是父進程還是子進程。
使用fork函數得到的子進程是父進程的一個復制品,它從父進程處復制了整個進程的地址空間,包括進程上下文,進程堆棧,內存信息,打開的文件描述符,信 號控制設定,進程優先級,進程組號,當前工作目錄,根目錄,資源限制,控制終端等。而子進程所獨有的只是它的進程號,資源使用和計時器等。可以看出,使用 fork函數的代價是很大的,它復制了父進程中的代碼段,數據段和堆棧段里的大部分內容,使得fork函數的執行速度并不快。
創建一個進程,至少涉及的函數:
sys_clone, do_fork, dup_task_struct, copy_process, copy_thread, ret_from_fork
創建一個新進程在內核中的執行過程
- fork、vfork和clone三個系統調用都可以創建一個新進程,而且都是通過調用do_fork來實現進程的創建
- 新的進程是從ret_from_fork處開始執行的,在ret_from_fork函數中,首先,子進程通過copy process函數和dup_task_struct函數復制父進程的狀態,并通過alloc_thread_infonode將父進程的堆棧狀態壓入子進程的堆棧以備返回父進程時候使用,確保了內核進程的執行起點與內核堆棧保持一致,最后,跳轉至sys_call exit函數,完成子進程的創建。
- Linux通過復制父進程來創建一個新進程,那么這就給我們理解這一個過程提供一個想象的框架
- 復制一個PCB——task_struct
err = arch_dup_task_struct(tsk, orig);
- 要給新進程分配一個新的內核堆棧
ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //這里只是復制thread_info,而非復制內核堆棧
1 *childregs = *current_pt_regs(); //復制內核堆棧
2 childregs->ax = 0; //為什么子進程的fork返回0,這里就是原因!
3
4 p->thread.sp = (unsigned long) childregs; //調度到子進程時的內核棧頂
5 p->thread.ip = (unsigned long) ret_from_fork; //調度到子進程時的第一條指令地址
瀏覽進程創建過程相關的關鍵代碼
- 系統調用內核處理函數sys_fork、sys_clone、sys_vfork
最終都是執行do_fork()
do_fork()里的復制進程的函數:
復制PCB:
打開復制PCB的具體函數:
打開alloc_thread_info():
拷貝內核堆棧數據和指定新進程的第一條指令地址。
創建的新進程是從哪里開始執行的?
(1)復制內核堆棧時
打開pt_regs:
int指令和SAVE_ALL壓到內核棧的內容。
下面分析entry_32.S,也就是總控程序。
實驗部分:
添加fork()到MenuOS
編譯并啟動MenuOS
用GDB連接,添加breakpoints,根據觀察copy_process是建立新進程,
weak_up_new_task則是運行這個新進程,所以要嘗試添加這樣一個斷點
breakpoints list:b sys_clone
b sys_clone
b do_fork
b copy_process
b dup_task_struct
b alloc_task_struct_node
b arch_dup_task_struct
b copy_thread
b ret_from_fork
b wake_up_new_task
跟蹤fork執行
總結:
進程創建是linux系統調用的一種形式,進程創建可以通過fork,vfork,clone三個系統調用實現,它們都是通過do_fork函數實現進程創建的,在do_fork函數中,以ret_from_fork函數為執行起點,復制父進程的內存堆棧和數據,并修改某些參數實現子進程的定義和初始化,創建子進程的工作完成后,通過sys_call exit函數退出并pop父進程的內存堆棧,實現新進程的創建工作。
fork創建的新進程是和父進程(除了PID和PPID)一樣的副本,包括真實和有效的UID和GID、進程組合會話ID、環境、資源限制、打開的文件以及共享內存段。
根據代碼的分析,do_fork中,copy_process管子進程運行的準備,wake_up_new_task作為子進程forking的完成。