今天開始整理記錄linux操做系統相關知識,一方面是對上半年學習操做系統的總結,另外一方面留着看成未來應對考試面試的複習筆記linux
用通俗易懂的話來說,進程就是處於執行期的程序。程序是一組計算機能識別的執行的指令集合,程序不是進程,執行中的程序纔是進程。但進程可不單單侷限於一段可執行程序代碼,其一般還要包含其餘資源,好比打開但文件、掛起的信號、數據段、處理器狀態、一個具體內存映射的內存地址空間以及一個或者多個執行線程等等......實際上,進程就是正在執行的程序代碼的實時結果。(線程是在進程中活動的對象,是linux內核調度的對象。linux系統對線程和進程並不特別區分,對linux而言線程就是一種特殊對進程。)面試
在現代操做系統中,進程提供兩種虛擬機制:虛擬處理器和虛擬內存。雖然實際上許多進程在分享一個處理器,但虛擬處理器給進程一種假象,能讓進程以爲本身在獨享一個處理器;而虛擬內存則讓進程在分配和管理內存時以爲本身擁有整個系統的全部內存資源。(在線程之間能夠共享虛擬內存,但每一個線程都擁有各自都虛擬處理器)數據結構
進程在建立的時刻開始存活,在linux中,這是經過調用fork()的結果。該函數經過複製一個現有的進程來建立一個新的進程。調用fork()都進程叫作父進程,新產生的進程叫作子進程。在該調用結束時,在返回的代碼段的位置上,父進程恢復執行,子進程開始執行。fork()系統調用從內核返回兩次:一次回到父進程中,另外一次回到新的子進程.函數
//fork()實例 #include<unistd.h> #include<stdio.h> #include<stdlib.h> int main(int argc,char *argv[]){ pid_t pid=fork(); if ( pid < 0 ) { fprintf(stderr,"錯誤!"); } else if( pid == 0 ) { printf("子進程空間"); exit(0); } else { printf("父進程空間,子進程pid爲%d",pid); } // 可使用wait或waitpid函數等待子進程的結束並獲取結束狀態 exit(0); }
一般,新建立的進程都是爲了當即執行新的、不一樣的程序,而接着調用exec()這組函數,這組函數能建立新的地址空間,並把新的程序載入其中。linux內核中,fork()實際是由clone()系統調用實現的(這裏不討論clone())。學習
最後,程序經過exit()系統調用進行退出。這個函數會終結進程並將其佔用的資源所有釋放掉。父進程能夠經過wait()系統調用查詢子進程是否終結,使得進程擁有等待特定進程執行完畢的能力。進程退出後被設爲僵死狀態(此時的進程稱爲殭屍進程),直到父進程調用wait()或者waitpid()爲止(這個過程又叫收屍)spa
內核把進程的列表存放在叫作任務隊列的雙向循環列表中。鏈表中每一項都是類型爲task_struct的結構,這個結構就是進程描述符。(學過操做系統就知道,爲了描述控制進程的運行,系統中存放進程的管理和控制信息的數據結構稱爲程序控制塊(PCB),每一個進程都有一個PCB。在linux中這個PCB就是task_struct結構體)進程描述符包含一個具體進程的全部信息,進程描述符中包含的數據能完整的描述一個進程正在執行的程序:打開的文件、進程的空間地址、掛起的信號、進程的狀態以及其餘更多的信息。這裏不做詳細介紹,想深刻學習能夠參考這篇博文:https://blog.csdn.net/lf_2016/article/details/54347820操作系統
內核經過一個惟一的進程標示值或者PID來標示每一個進程。PID是一個數,是一個int類型,PID默認的最大值爲32768(能夠經過修改文件來調整最大值),這個最大值實際上就是系統中容許同時存在的進程的最大數目。內核把每一個PID存放在它們各自的進程描述符中。.net
進程描述符中經過state成員描述進程當前狀態,系統中每一個進程必然處於五種進程中的一種,該成員的值也必爲五種狀態之一:線程
TASK_RUNNING(運行)——進程是可執行的;它或者正在執行,或者在運行隊列中等待執行。這是進程在用戶空間執行的惟一可能狀態。調試
TASK_INTERRUPTIBLE(可中斷)——進程正在睡眠(也就是說進程被阻塞),等待某些條件的達成。一旦這些條件達成,內核就會把進程狀態設爲運行。處於此狀態的進程也會由於接收到信號而提早被喚醒。
TASK_UNINTERRUPTIBLE(不可中斷)——除了就算接收到信號也不會被喚醒外,這個狀態與可中斷狀態相同。這個狀態一般在進程必須在等待時不受干擾或等待事件很快發生時出現。
__TASK_TRACED ——被其餘進程跟蹤的進程,例如經過GDB對調試程序進行跟蹤。
__TASK_STOPPED(中止)——進程中止執行。
許多操做系統都提供了產生進程的機制,首先在新的地址空間裏建立進程,讀入可執行文件,最後開始執行。linux把上述步驟分解到兩個單獨的函數fork()和exec()中。首先fork()用過拷貝當前進程建立一個子進程,子進程與父進程的區別僅僅在於PID、PPID(父進程的進程號)和某些資源和統計量(例如掛起的信號)。exec()函數負責讀取可執行文件並將其載入地址空間開始運行。
傳統的的fork()系統調用直接把全部資源複製給新建立的進程。這種實現過程過於簡單且效率低下,由於它拷貝的數據或許並不共享,若是新進程打算當即執行一個新的映像,那麼這些拷貝就是作無用功。linux 的 fork()改進了這一缺點,使用了寫時拷貝頁(copy-on-write)實現。寫時拷貝是一種能夠推遲甚至免除拷貝數據的技術。內核並不複製整個進程地址空間,而是讓父、子進程共享同一個拷貝。
只有在須要寫入的時候,數據纔會被複制,從而使各個進程擁有各自的拷貝。也便是說,資源的複製只有在須要寫入的時候才進行,在這以前,只是以只讀的方式共享。這種技術使地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行。在頁面不會被寫入的狀況下(如fork()以後當即調用exec()),它們就無需複製。
當一個進程終結時,內核必須釋放它所佔有的資源並告知其父進程。它一般發生在進程調用exit()系統調用時,既可能顯式調用這個系統調用,也可能隱式地從某個函數的主函數返回(c語言編譯器會在main()函數返回點後面調用exit()代碼)。調用完成後,進程相關聯的全部資源都被釋放掉。進程不可運行並處於EXIT_ZOMBIE退出狀態。此時進程存在的惟一目的就是向他的父進程提供信息,父進程檢索到信息後,由進程所持有的剩餘內存被釋放,歸還給系統使用。(在父進程得到終止的進程信息前,子進程的task_struct是不會釋放的)父進程調用wait()或者waitpid()回收子進程的信息