wait函數和waitpid函數
1. 殭屍進程
說明
- 子進程結束但是沒有完全釋放內存(在內核中的task_struct沒有釋放),該進程就會成爲殭屍進程
- 當殭屍進程的父進程結束後就會被init進程(1號進程)接管,最終被回收
殭屍進程的危害
- 如果你不處理殭屍進程的話,那麼保留的那段信息就不會釋放,其進程號就會一定被佔用,但是系統所能使用的進程號是有限的,如果大量的產生殭屍進程,將因爲沒有可用的進程號而導致系統不能產生新的進程
避免殭屍進程
- 讓殭屍進程的父進程來回收,父進程每隔一段時間來查詢子進程是否結束並被回收,調用wait或者waitpid函數,通知內核釋放殭屍進程
- 採用信號SIGCHLD通知處理,並在信號處理程序中調用wait函數
- 讓殭屍進程成爲孤兒進程,並有init進程回收
2.避免殭屍進程方法一說明
- 頭文件
#include <sys/types.h>
#include <sys/wait.h>
區別
- 在一個進程終止前,wait 使其調用者阻塞
- waitpid 函數有一個選擇項,可以使調用者不阻塞
- waitpid 等待一個指定的子進程,wait 等待所有的子進程,返回任一子進程的終止狀態
參數
status參數
- 爲空時,代表任意狀態結束的子進程,若不爲空,則代表指定狀態結束的子進程
options參數
- WNOHANG:若由pid指定的子進程沒有退出則立即返回,則waitpid不阻塞,此時返回值爲0
- WUNTRACED:若某實現支持作業控制,則由pid指定的任一子進程狀態已暫停,且其狀態自暫停以來還未報告過,則返回其狀態
檢查 wait 和 waitpid 函數返回終止狀態的宏(前面判斷,後面獲得狀態碼)
- WIFEXITED/WEXITSTATUS(status):若爲正常終止子進程的返回的狀態,則爲真
- WIFSIGNALED/WTERMSIG(status):若爲異常終止子進程的返回的狀態,則爲真(接到一個不能捕捉的信號)
- WIFSTOPED/WSTOPSIG(status):若爲當前暫停子進程的返回的狀態,則爲真(如果當前進程在終止前暫停過,則獲得暫停的狀態碼)
2. wait函數
- 原型:
pid_t wait(int *status)
- 返回:成功返回子進程ID,出錯返回-1
- 作用:等待子進程退出並回收,防止殭屍進程產生
3. waitpid函數
- 原型:
pid_t waitpid(pid_t pid, int *status, int options)
- 返回:成功返回子進程ID,出錯返回-1
- 功能:wait函數的非阻塞版本
- pid參數:
- pid == -1:等待任一子進程,與功能 wait 相等
- pid > 0:等待其進程ID與 pid 相等的子進程
- pid == 0:等待其組ID等於調用進程的組ID的任一子進程
- pid < -1:等待其組ID等於 pid 的絕對值的任一子進程
4. 示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
void out_status(int status){
if(WIFEXITED(status)){
//正常終止
printf("normal exit: %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
//異常終止
printf("abnormal term: %d\n", WTERMSIG(status));
}
else if(WIFSTOPPED(status)){
//終止前暫停或者等待過
printf("stopped sig: %d\n", WSTOPSIG(status));
//kill -19 測試結果
}
else{
printf("unknow sig\n");
}
}
int main(void){
int status;
pid_t pid;
if((pid = fork()) < 0){
perror("fork error");
exit(1);
}
else if(pid == 0){
printf("pid: %d, ppid: %d\n", getpid(), getppid());
exit(3); //子進程終止運行
}
//父進程阻塞,等待子進程結束並回收
wait(&status);
out_status(status);
printf("--------------------------\n");
if((pid = fork()) < 0){
perror("fork error");
exit(1);
}
else if(pid == 0){
printf("pid: %d, ppid: %d\n", getpid(), getppid());
int i = 3, j = 0;
int k = i / j; //異常測試
printf("k: %d\n", k);
}
wait(&status);
out_status(status);
printf("--------------------------\n");
if((pid = fork()) < 0){
perror("fork error");
exit(1);
}
else if(pid == 0){
printf("pid: %d, ppid: %d\n", getpid(), getppid());
pause(); //暫停測試
}
do{
//暫停測試需要用waitpid來捕獲暫停的信號,並返回
pid = waitpid(pid, &status, WNOHANG | WUNTRACED);
if(pid == 0){
sleep(1);
}
}while(pid == 0);
out_status(status);
return 0;
}
運行測試
運行程序
發送信號給程序
測試結果