進程的描述和進程的創建

進程的描述

進程控制塊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; //調度到子進程時的第一條指令地址

瀏覽進程創建過程相關的關鍵代碼

  1. 系統調用內核處理函數sys_fork、sys_clone、sys_vfork


最終都是執行do_fork()

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的完成。

如果有幫助到了您,記得打賞鼓勵下作者哦....

收款碼.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容