進程:計算機中資源分配的最小單元。 進程可以是從命令行執行的一個命令(短期),也可以是一種網絡服務(長期)。 對進程及其調度進行管理顯得十分重要。 單CPU的計算機在一個時間片內只能執行一條指令。 進程調度(PRocess Scheduling): 首先為每一個進程指派一定的運行時間,這個時間通常非常短,短到以毫秒為單位,然后依照某種規則,從眾多進程中挑選一個投入運行,其他進程暫時等待,當正在運行的那個進程時間耗盡,或執行完畢退出,或因某種原因暫停,linux就會重新進行調度,挑選下一個進程投入運行。因為每個進程占用的時間片都很短,從使用者角度來看,就好像多個進程同時運行一樣。 在Linux中,每個進程在創建時都會被分配一個數據結構,稱為程序控制塊(PCB)。 PCB中最重要的是進程ID(PID),PID也被稱為進程標識符,是一個自然數,在Linux操作系統中唯一地標志一個進程。
進程分類: 1、 交互進程; 2、 批處理進程; 3、 守護進程; 守護進程總是活躍的,一般在后臺運行,守護進程有系統在開機時通過腳本自動激活啟動或超級用戶root來啟動。 由于守護進程是一直運行著的,所以它所處的狀態是等待請求處理任務。
進程的屬性: 1、 進程ID(PID):是唯一的數值,來區分進程; 2、 父進程和父進程的ID(PPID); 3、 啟動進程的用戶ID(UID)和所歸屬的組(GID); 4、 進程狀態:運行R、休眠S、僵尸Z; 5、 進程執行的優先級; 6、 進程所連接的終端名; 7、 進程資源占用,如占用資源大小(內存、CPU占用量)。
父進程和子進程: 父進程和子進程的關系是管理和被管理的關系,當父進程終止時,子進程也隨之終止;但子進程終止,父進程并不一定終止。 在進程管理中,當我們發現占用資源過多,或無法控制進程時,應該殺死它,以保證系統的穩定安全運行。
Linux進程的三態 一般情況下,一個運行進程必須具有以下三種狀態: 1、 就緒狀態:當進程已分配到除了CPU以外所有必要的資源,只要獲得處理器便可立即執行,這時的進程狀態稱為就緒狀態; 2、 執行狀態:當進程已獲得處理器,其程序正在處理器上執行,此時的進程狀態成為執行狀態; 3、 等待狀態(阻塞):正在執行的進程,由于等待某個事件 三種狀態之間的轉換: 一個進程在運行期間,會不斷地從一種狀態轉換到另一種狀態,它可以多次處于就緒狀態和執行狀態,也可以多次處于阻塞狀態。 1、 就緒 ? 執行 :處于就緒狀態的進程,當進程調度程序為之分配了處理器之后,該進程便由就緒狀態轉變為執行狀態; 2、 執行 ? 就緒 :處于執行狀態的進程在其執行過程中,因分配給它的一個時間已用完而不得不讓出處理器,于是進程從執行狀態轉變成就緒狀態; 3、 執行 ? 阻塞 :正在執行的進程因等待某種事件發生而無法繼續執行,便從執行狀態變成阻塞狀態; 4、 阻塞 ? 就緒 :處于阻塞狀態的進程,若其等待的事件已經發生,于是進程由阻塞狀態轉變為就緒狀態。
Linux進程調度 任何時刻只能有一個進程或者線程處于執行狀態,因此OS需要決定哪個進程執行,哪些進程等待,這就是進程的調度。 程序使用CPU的三種模式: IO密集型、計算密集型、平衡型 IO密集型:響應時間非常重要; 計算密集型:CPU周轉時間比較重要; 平衡型:響應和周轉時間的平衡是最重要的。
調度算法:
在使用fork()創建一個進程時,子進程只是完全復制父進程的資源,復制出來的子進程有自己的task_struct結構和PID,但卻復制了父進程其他所有的資源。這樣得到的子進程獨立于父進程,具有良好的并發性,但是兩者之間的通信需要專門的通信機制。 通過fork()創建的子進程,需要將上面描述的每一種資源都復制一個副本。這樣看來,fork是一個開銷非常大的系統調用,然而這些開銷并不是在所有情況下都是必須的。 fork()調用執行一次返回兩個值,對于父進程,fork()返回子進程的進程號,而對于子進程fork()返回0,這就是一個函數返回兩次的本質。 在fork()之后,子進程和父進程都會繼續執行fork()調用之后的命令。子進程是父進程的副本,它將獲得父進程的數據空間、堆和棧的副本,這些都是副本,父進程和子進程不共享這部分內存。也就是說,子進程對父進程中的同名變量修改不會影響它在父進程中的值。但是子進程和父進程又共享一些東西,簡單地說就是程序的正文段。正文段存放著由CPU執行的機器指令,通常是read-only類型的。
vfork()會產生一個新的子進程,其子進程會復制父進程的數據與堆棧空間,并繼承父進程的用戶代碼、組代碼、環境變量、已打開的文件代碼、工作目錄和資源限制等。
注意:vfork()創建的子進程必須調用exit()來結束,否則子進程將不可能結束。
啟動進程:exec族 int execl(const char *path , const char *arg , ……); int execlp(const char *file , const char *arg , …….); int execle(const char *path , const char *arg , …… , char *const envp[]); int execv(const char *path , char *const argv[]); int execvp(const char *file , char *const argv[]); int execve(const char *path , char *const argv[] , char *const envp[]); 以上函數只有execve是真正的系統調用,其他都是在此基礎上經過包裝的庫函數。
exec函數族的作用是根據指定的文件名找到可執行的文件,并用它來取代調用進程的內容。換句話說,也就是在調用進程內部執行一個可執行文件。這里的可執行文件既可以是二進制文件,也可以是任何Linux下可執行的腳本文件。 與一般情況不同,exec函數族的函數執行成功后不會返回,因為調用進程的實體,包括代碼段、數據段和堆棧等都已經被新的內容所取代,只有進程ID等一些表面的信息任然保持原樣。只有調用失敗了,它們才會返回-1,從源程序的調用點接著往下執行。
system用于執行shell命令。 system()會調用fork()產生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令,此命令執行完后隨即返回原調用的進程。在調用system()期間SIGCHLD信號會被暫時擱置,SIGINT和SIGQUIT信號則會被忽略。
新聞熱點
疑難解答