本小結記錄下進程的學習。
進程要有四個重要的系統函數,分別是fork(),exit(),wait(),exec系列函數。
首先來看fork,函數原型為pid_t fork();主要用於一個進程(父進程)創建一個新進程(子進程)。子進程幾乎是父進程的翻版,它擁有和父進程相同的堆,棧,數據段,執行文本段(程序段)。但是在內部實現的時候,子進程並不直接復制父進程的這些內容,因為大多數情況下,創建一個子進程之後,子進程就執行exec系列函數,堆,棧什麼的就要改變了,所以開始直接從父進程復制過來很浪費,真實情況是父,子進程共享同一代碼段。系統調用fork()為子進程創建代碼段時,其進程級頁表項指向和父進程相同的物理內存頁幀。對於數據段,堆,棧,采用寫時復制技術。修改時,再各自拷貝。
一般可以通過fork()返回值區分父,子進程。父進程中,fork返回子進程ID,子進程,返回0.-1代表出錯。
父,子進程可以實現文件共享,他們維持同樣的打開文件句柄。這些打開的文件句柄包括文件偏移量,和文件狀態標志。如果子進程改變了文件的偏移量,也會影響到父進程的相應描述符。但要注意,可能會有競爭現象。因為不確定父進程還是子進程先執行。
在進程終止的時候,調用exit()函數,該函數會刷新stdio流緩沖區,並將狀態保存在status中。void exit(int status),雖然status為int型,它僅有低8位可供父進程使用。0表示進程正常退出,非0表示進程異常退出。
父進程可以用wait函數來等待任一子進程。
[code]pid_t wait(int *status)如果調用之前並沒有子進程終止,調用就一直阻塞,直到子進程終止,如果調用的時候子進程終止已經終止,wait立即返回該僵屍進程的狀態。
如果status非空,子進程終止消息通過status所指的整形變量返回。
wait返回終止的子進程pid號。
由於wait只能阻塞等待子進程終止,而且不能等待特定子進程終止,由此有了wait函數的升級版。
如果wait()不關心子進程的退出狀態,那麼可以將status設置為NULL即可。
[code]pid_t waitpid(pid_t pid,int *status,int optition)wait在子進程未退出之前,父進程一直阻塞等待,而且無法等待一個特定的子進程退出,它只是等待下一個子進程退出。
waitpid會暫停目前進程的運行,直到信號來到或者子進程結束。
如果不在意子進程返回的狀態,可以將status設置為NULL。
其中optition有一些選項可以控制選擇,可以為0或者一些其它。其中,設置為WNOHANG代表若子進程結束返回其pid,若沒結束,則立即返回,返回0.
pid即為需要等待的具體子進程;
if pid>0,表示等待ID為pid號的進程
if pid=0,則等待與父進程同進程組的所有子進程。
if pid<-1,等待與pid絕對值相等的所有子進程
if pid==-1 等待任意子進程,與wait()等價。
孤兒進程:
一個子進程的父進程先掛了,它就成了孤兒,就成了孤兒進程。之後該子進程將有進程號為1的進程之祖—-init收養。
僵屍進程:
如果父進程在調用wait去獲取子進程的退出狀態之前,子進程已經掛了。那麼父進程怎麼去獲得子進程的狀態呢。內核通過將子進程轉換為僵屍進程來處理。子進程死而不僵,內核釋放它大部分資源,在進程表中保存一條記錄,記錄下子進程的ID,終止狀態,資源數據等等。保證父進程可以wait()回收。那麼如果父進程就根本就沒有調用wait怎麼辦呢?直接將子進程拋棄了,那麼只有等待父進程也掛了,子進程由init接管,它會自動調用wait()。那如果父進程他一直老不死怎麼辦?比如一些網絡服務器,它創建眾多服務子進程,自己不死,那麼系統長久運行就有很多僵屍進程在進程表裡,阻礙新進程創建了。因此,在長壽的父進程中,一定要調用wait()進行回收子進程。
SIGCHLD在子進程退出時,默認會發送該信號給父進程,默認情況下,父進程會忽略此信號,但是為了知道子進程何時退出,可以捕捉該信號。
execve()函數
int execve(const char *pathname,char *const argv[],char *const envp[])
pathname 為准備載入當前進程空間的新程序的路徑名,
argv為傳遞給新進程的命令行參數,由字符串指針組成,以NULL結束。
最後一個參數envp制定新程序 的環境列表,也以NULL結束,字符串格式為name=value。
當然還有一些exec系列函數,本質是一樣的,只是提供的接口不一樣而已,在此就不一一列出了。