20135307張嘉琪linux
進程就是處於執行期的程序(目標碼存放在某種存儲介質上),但進程並不只僅侷限於一段可執行程序代碼。一般進程還要包含其餘資源,像打開的文件,掛起的信號,內核內部數據,處理器狀態,一個或多個具備內存映射的內存地址空間及一個或多個執行線程。固然還包括用來存放全局變量的數據段等,實際上,進程就是正在執行的程序代碼的實時結果,內核須要有效而又透明地管理全部細節。算法
執行線程,簡稱線程,是在進程中活動的對象,每一個線程都擁有一個獨立的程序計數器、進程棧和一組進程寄存器,內核調度的對象是線程,而不是進程,在傳統的Linux系統中,一個進程只包含一個線程,但如今的系統中,包含多個線程的多線程程序司空見慣。Linux系統的線程實現很是特別:它對線程和進程並不特別區分,編程
對Linux而言,線程只不過是一種特殊的進程罷了。緩存
在現代操做系統中,進程提供兩種虛擬機制:虛擬處理器和虛擬內存。數據結構
在現代Linux內核中,fork()其實是由clone()系統調用實現的。多線程
內核把進程的列表存放在叫作任務隊列的雙向循環鏈表中。併發
鏈表中的每項都是類型爲task_struct、稱爲進程描述符的結構,該結構定義在<linux/sched.h>文件中。函數
內核經過一個惟一的進程標識值或PID來標識每一個進程,內核把每一個進程的PID存放在它們各自的進程描述符中。優化
在內核中,訪問任務一般須要得到指向其taskstruct的指針,實際上,內核中大部分處理進程程描述符的速度就顯得尤其重要。硬件體系結構不一樣,該宏的實現也不一樣,它必須針對專門的硬件體系結構作處理,有的硬件體系結構能夠拿出―個專門寄存器來存放指向當前進程taskstruct的指針,用於加快訪問速度。操作系統
Unⅸ系統的進程之間存在—個明顯的繼承關係,在Linux系統中也是如此。全部的進程都是PID爲1的init進程的後代。內核在系統啓動的最後階段啓動init進程。該進程讀取系統的初始化腳本並執行其餘的相關程序,最終完成系統啓動的整個過程。
傳統的fork()系統調用直接把全部的資源複製給新建立的進程,這種實現過於簡單而且效率低下,由於它拷貝的數據也許並不共享,更糟的狀況是,若是新進程打算當即執行一個新的映像,那麼全部的拷貝都將前功盡棄。Linux的fork()使用寫時拷貝頁實現,寫時拷貝是一種能夠推遲甚至免除拷貝數據的技術。內核此時並不複製整個進程地址空間,而是讓父進程和子進程共享同一個拷貝。
只有在須要寫入的時候,數據纔會被複制,從而使各個進程擁有各自的拷貝,也就是說資源的複製只有在須要寫入的時候才進行,在此以前,只是以只讀方式共享,這種技術使地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行在頁根本不會被寫入的狀況下它們就無須複製了。
fork()的實際開銷就是複製父進程的頁表以及給子進程建立惟一的進程描述符。在通常狀況下,進程建立後都會立刻運行一個可執行的文件,這種優化能夠避免拷貝大量根本就不會被使用的數據(地址空間裏經常包含數十兆的數據)因爲Unix強調進程快速執行的能力,因此這個優化是很重要的。
copy_process()完成的工做:
線程機制是現代編程技術中經常使用的一種抽象概念,該機制提供了在同―程序內共享內存地址空間運行的―組線程,這些線程還能夠共享打開的文件和其餘資源,線程機制支持併發程序設計技術,在多處理器系統上,它也能保證真正的並行處理。 Linux實現線程的機制很是獨特,從內核的角度來講,它並無線程這個概念,Linux把全部的線程都當作進程來實現,內核並無準備特別的調度算法或是定義特別的數據結構來表徵線程,相反,線程僅僅被視爲―個與其餘進程共享某些資源的進程,每一個線程都擁有惟一隸屬於本身task_struct,因此在內核中,它看起來就像是一個普通的進程(只是線程和其餘一些進程共享某些資源,如地址空間)。
進程的建立與普通進程的建立相似,只不過在調用clone()時須要傳遞一些參數標誌來指明所須要共享的資源。
內核常常須要在後臺執行一些操做,這種任務能夠經過內核線程完成——獨立運行在內核空間的標準進程。內核線程和普通的進程間的區別在於內核線程沒有獨立的地址空間。它們只在內核空間運行,歷來不切換用戶空間去,內核進程和普通進程同樣,能夠被調度,也能夠被搶佔。 Linux確實會把一些任務交給內核線程去作,像flush和ksofirqd這些任務就是明顯的例子,在裝有Linux系統的機子上運行ps -ef命令,你能夠看到內核線程,有不少!這些線程在系統啓動時由另一些內核線程建立,實際上,內核線程也只能由其餘內核線程建立,內核是經過從kthreadd內核進程中衍生出全部新的內核線程來自動處理這一點的,在<linux/kthreadd>中申明有接口。
當一個進程終結時,內核必須釋放它所佔有的資源並把這一不幸告知其父進程。
若是父進程在子進程以前退出,必須有機制來保證子進程能找到一個新的父親不然這些成爲孤兒的進程就會在退出時永遠處於僵死狀態,白白地耗費內存。前面的部分已經有所暗示於這個問題,解決方法是給子進程在當前線程組內找—個線程做爲父親,若是不行就讓init作它們的父進程。
在本章中,咱們考察了操做系統中的核心概念——進程,咱們它爲什麼如此重要,以及進程與線程之間的關係,然也討論了進程的通常特性,而後,討論了Linux如何存放和表示進程,如何建立進程,如何把新的執行映像裝入到地址空間,如何表示進程的層次關係,父進程又是如何收集其後代的信息以及進程最終如何消亡。
進程是一個很是基礎、很是關鍵的抽象概念,位於每一種現代操做系統的核心位置,也是咱們擁有操做系統(用來運行程序)的最終緣由。