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