一、進程的退出
(一)僵尸進程與孤兒進程
(二)相關函數
1、exit函數
2、_exit函數
3、atexit函數
二、進程空間的回收(相關函數)
1、wait函數
2、waitpid函數
3、練習
4、exec族
5、system函數
一、進程的退出
(一)僵尸進程與孤兒進程
1、僵尸進程:進程執行結束但空間未被回收變成僵尸進程(pcb不釋放會導致內存越來
????????????????????????越小導致內存崩潰);
2、孤兒進程:父進程先消亡。
(二)相關函數
1、exit函數
(1)函數原型:
void exit(int status)
//調用:
exit(1);
(2)功能:讓進程退出,并刷新緩存區
(3)參數:status:進程退出的狀態
(4)返回值:缺省
????????EXIT_SUCCESS 0
????????EXIT_FAILURE 1
(5)屬于庫函數
????????退出狀態,終止的進程會通知父進程,自己使如何終止的。如果是正常結束(終
止),則由exit傳入的參數。如果是異常終止,則有內核通知異常終止原因的狀態。任
何情況下,父進程都能使用wait,waitpid獲得這個狀態,以及資源的回收。
(6)相當于return
????????但當該關鍵字出現在main函數中的時候可以結束進程
????????如果在其他函數中則表示結束該函數;
(7)注意:不管函數調用層次多深,調用exit函數后當前進程立馬結束;
????????????????????全面回收工作:文件關閉、堆釋放、緩沖區清理。
(8)示例:
2、_exit函數
? 該函數屬于系統調用;
(1)函數原型:void?_exit(int?status);
(2)功能:讓進程退出,不刷新緩存區(只會關閉已打開的文件,其他不管)
(3)參數:status:進程退出狀態
(4)返回值:缺省
(5)示例:
3、atexit函數
回調函數
(1)函數原型:int?atexit(void?(*function)(void));
(2)功能:注冊進程退出前執行的函數
(3)參數:function:函數指針,指向void返回值void參數的函數指針
(4)返回值:成功返回0,失敗返回非0
(5)注:當程序調用exit或者由main函數執行return時,所有用atexit注冊的退出函數,將
? ? ? ? ? ? ? ? 會由注冊時順序倒序被調用
(6)示例:
二、進程空間的回收(相關函數)
1、wait函數
(1)函數原型:pid_t wait(int *status);
(2)功能:該函數可以阻塞等待任意子進程退出并回收該進程的狀態;
?????????????????????一般用于父進程回收子進程狀態。
(3)參數
????????status 進程退出時候的狀態(退出狀態(32Bit)包括退出值(8Bit)):
????????????????如果不關心其退出狀態一般用NULL表示;
????????????????如果要回收進程退出狀態,則用WEXITSTATUS回收。
(4)返回值:成功 回收的子進程pid;失敗 -1;
(5)宏:
WIFEXITED(status) 判斷是不是正常結束
WEXITSTATUS(status) 若正常結束則使用這個宏去返回狀態
WIFSIGNALED(status) 判斷是不是收到了信號而終止的
WTERMSIG(status)如果是信號終止的,那么是幾號信號
(6)注意:
????????如果所有的子進程都在運行,在阻塞(進程運行過程中,某條件未滿足導致當前進
程被迫進入等待狀態);
????????如果一個子進程終止,正在等待的父進程則獲得終止狀態,獲得子進程的狀態后,
?立刻返回。
????????如果沒有子進程,則立即出錯退出。
waitpid(-1,status,0)=wait(status);
(7)示例:
2、waitpid函數
(1)函數原型:pid_t waitpid(pid_t pid, int *status, int options);
(2)pid:
小于-1 回收指定進程組內的任意子進程;
等于-1 回收任意子進程,組內外;
等于0 回收和當前調用waitpid一個組的所有子進程,組內;
大于0 回收指定ID的子進程;
waitpid (-1,a,0) == wait(a);
(3)status:子進程退出時候的狀態
????????如果不關注退出狀態用NULL;
(4)options 選項:0 表示回收過程會阻塞等待;
????????????????????????????????WNOHANG 表示非阻塞模式回收資源。
(5)返回值:成功 返回接收資源的子進程pid
????????????????????????失敗 -1,0
(6)示例:
3、練習
????????設計一個多進程程序,用waitpid函數指定回收其中的某個進程資源并將其狀態打
印輸出。其他的進程都以非阻塞方式進行資源回收。
運行結果:
4、exec族
啟動一個已存在的外部程序
(1)用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分
支),子進程往往要調用一種exec函數以執行另一個程序。當進程調用一種exec函時,
該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程開始執行。調用
exec并不創建; 新進程,所以調用exec前后該進程的id并未改變。
(2)進程角度:代碼段被替換(老進程被新進程替換)
新代碼段走完后,a.out結束
(3)六種以exec開頭的函數,統稱exec函數;
a.函數原型:
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);int execle(const char *path, const char *arg, ..., char *const envp[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);int execve(const char*path,char*const argv[],char*const evnp[]);
int execlp(const char *file, const char *arg, ...);int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
b.函數區別
1)前4個使用路徑名作為參數,后面兩個使用文件名做參數
? ? ?當filename中,含有/時視為路徑名,否則就按PATH變量,在指定目錄下查找可執行
文件。
2)相關的參數表傳遞,l表示list,v表示vector;
execl,execlp,execle,需要將參數一個一個列出,并以NULL結尾。
execv,execvp,execve,需要構造一個參數指針數組,然后將數組的地址傳入。
3)以e結尾的函數,可以傳入一個指向環境字符串的指針數組的指針。其他未指定環境
變量,使用父進程繼承過來的。execve?是真正的系統調用
4)這些函數如果調用成功則加載新的程序從啟動代碼開始執行,不再返回,如果調用
出錯則返回-1,所以exec函數只有出錯的返回值而沒有成功的返回值。
c.示例:
5、system函數
?該函數為系統函數
(1)函數原型:int?system(const?char?*command);?
(2)底層實現:fork+exec ,(快捷的exec)
(3)功能:使用該函數可以將shell命令直接在代碼中執行;
(4)參數:command:要執行的shell命令;
(5)返回值:成功?0,失敗?-1;
(6)調用:system("vim?1.c");