1.進程的概念 linux
進程就是處於執行期的程序(目標碼存放在某種存儲介質上〉。但進程並不只僅侷限於一段dom
可執行程序代碼( Unix 稱其爲代碼段, text section)。一般進程還要包含其餘資源,像打開的文件,掛起的信號,內核內部數據,處理器狀態, 一個或多個具備內存映射的內存地址空間及一個或多個執行線程( thread of execution ),固然還包括用來存放全局變量的數據段等。實際上,進程就是正在執行的程序代碼的實時結果。內核須要有效而又透明地管理全部細節。ide
2 進程描述符及任務結構函數
atruct t hread_info {優化
struct task_struct • task;操作系統
struct exec_domain • exec_d。main;線程
_ u32 flags;指針
_ u32 status;rest
_u32 cpu;blog
int preempt_ count;
mm_segment_t addr_limit;
struct restart bl。ck restart bl。ck;
v。id •sysenter_return;
int uaccess_err;}
3.進程描述符的存放
內核經過一個惟一的進程標識值(process identification value)或PID 來標識每一個進程。PID 是一個數,表示爲pid_t 隱含類型θ,實際上就是一個四類型。爲了與老版本的Unix 和Linux 兼容,PID 的最大值默認設置爲32768 (sh臼t int 短整型的最大值〉,儘管這個值也能夠增長到高達400 萬〈<linux/reads.h> 中所定義PID 最大值的限制〉。內核把每一個進程的PID 存放在它們各自的進程描述符中。
4進程建立
4.1 寫時拷貝
只有在須要寫入的時候,數據纔會被複制,從而使各個進程擁有各自的拷貝。也就是說,資
源的複製只有在須要寫入的時候才進行,在此以前,只是以只讀方式共享。這種技術使地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行。在頁根本不會被寫人的狀況下(舉例來講,fork()後當即調用exec(})它們就無須複製了。fork()的實際開銷就是複製父進程的頁表以反給予進程建立惟一的進程描述符。在通常狀況下,進程建立後都會立刻運行一個可執行的文件,這種優化能夠避免拷貝大量根本就不會被使用的數據〈地址空間裏經常包含數十她的數據〉。因爲Unix 強調進程快速執行的能力,因此這個優化是很重要的。
4. 2 fork()
Linux 經過clone()系統調用實現fork() 。這個調用經過一系列的參數標誌來指明父、子進程須要共享的資源〈關於這些標誌更多的信息請參考本章後面3.4 節〉。fork()、vfork()和一clone()庫函數都根據各自須要的參數楊L志去調用clone(),而後由clone()去調用do_fork().do_fork 完成了建立中的大部分工做,它的定義在kemeVfork.c 文件中。該函數調用copy_process()函數,而後讓進程開始運行。copy_process()函數完成的工做頗有意思:
l )調用dup_task_ struct()爲新進程建立一個內核枝、也read_info 結構和task_struct,這些值與當前進程的值相同。此時, 子進程和父進程的描述符是徹底相同的。
2 )檢查並確保新建立這個子進程後,當前用戶所擁有的進程數目沒有超出繪色分配的資源
的限制.
3 )子進程着孚使本身與父進程區別開來。進程描述符內的許多成員都要被清0 或設爲初始值.那些不是繼承而來的進程描述符成員,主要是統計信息。task_struct 中的大多數數據都依然未被修改.
4 ) 子進程的狀態被設置爲TASK_UNJNTERRUPTIBLE,以保證它不會投入運行。
5 ) copy _process()調用copy_flags()以更新task_struct 的組ags 成員.代表進程是否擁有超級用戶權限的PF_SUPE盯RIV 標誌被清0。代表進程尚未調用exec()函數的PF_FOR.KNOEXEC標誌被設置。
6 )調用alloc _pid()爲新進程分配一個有效的PID。
7 )根據傳遞給clone()的參數標誌, copy_process()拷貝或共享打開的文件、文件系統信息、信號處理函數、進程地址空間和命名空間等。在通常狀況下,這些資源會被給定進程的全部線程共享:不然,這些資源對每一個進程是不一樣的,所以被拷貝到這裏。的最後, copy_process()傲掃尾工做並返回一個指向子進程的指針。再回到do_fork()函數,若是copy_process()函數成功返回,新建立的子進程被喚醒並讓其投入運行。內核有意選擇子進程首先執行。.由於通常子進程都會立刻調用exec()函數,這樣能夠避免寫時拷貝的額外開銷,若是父進程首先執行的話,有可能會開始向地址空間寫入。
小結:在本章中,咱們考察了操做系統中的核心概念一一進程。咱們也討論了進程的通常特性,它爲什麼如此重要,以及進程與線程之間的關係。而後,討論了Linux 如何存放和表示進程(用task_ struct 和thread_info ),如何建立進程(經過fork(),實際上最終是clone()),如何把新的執行映像裝入到地址空間(經過execO 系統調用族〉,如何表示進程的層次關係,父進程又是如何收集其後代的信息(經過wait()系統調用族),以及進程最終如何消亡〈強制或自願地調用exit()) 。進程是一個很是基礎、很是關鍵的抽象概念,位於每一種現代操做系統的核心位置,也是咱們擁有操做系統(用來運行程序〉的最終緣由。