1. 程式人生 > >Linux系統程式設計---程序等待

Linux系統程式設計---程序等待

程序等待

1.為什麼需要程序等待

  1. 我們知道,子程序退出,父程序如果不管不顧,那麼子程序就可能變為殭屍程序,進而佔用系統資源,造成記憶體洩漏,
  2. 另外,程序一旦變成殭屍狀態,那就刀槍不入,“殺人不眨眼”的kill-9也無能為力,因為誰也辦法殺死一個已經死去的程序。
  3. 有時候我們需要知道父程序派給子程序的任務完成的如何如,子程序執行完成,結果對還對,或者是否正常退出。在這些情況,我們需要等待子程序執行完。
  4. 所以要想知道子程序的狀態,就需要父程序通過程序等待的方式,回收子程序資源,獲取子程序退出資訊。
    總結就是:防止產生殭屍程序,從而導致系統資源佔用和浪費,影響效能。

2.程序等待的方法

當一個程序正常或異常終止時,核心就向其父程序傳送 SIGCHLD 訊號,相當於告訴父親他哪個兒子掛了,而父程序可以通過 wait() 或 waitpid() 函式等待子程序結束,獲取子程序結束時的狀態,同時回收他們的資源(相當於,父親聽聽死去兒子的遺言同時好好安葬它)。

wait

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

返回值: 成功返回被等待程序pid,失敗返回-1。
引數status: 輸出型引數,獲取子程序退出狀態,不關心子程序的狀態則可以設定為NULL。


當引數status的值不是NULL,即當要獲取子程序的退出狀態時,需要用巨集來取它的退出資訊。由於wait和waitpid的status是一樣的,所以在下面會詳細介紹

功能: 等待任意一個子程序結束,如果任意一個子程序結束了,此函式會回收該子程序的資源。
注意:

  1. 在每個程序退出的時候,核心釋放該程序所有的資源、包括開啟的檔案、佔用的記憶體等。但是仍然為其保留一定的資訊,這些資訊主要主要指程序控制塊的資訊(包括程序號、退出狀態、執行時間等)。
  2. 呼叫 wait() 函式的程序會掛起(阻塞),直到它的一個子程序退出或收到一個不能被忽視的訊號時才被喚醒(相當於繼續往下執行)。
  3. 若呼叫程序沒有子程序,該函式立即返回;若它的子程序已經結束,該函式同樣會立即返回,並且會回收那個早已結束程序的資源。
  4. 所以,wait()函式的主要功能為回收已經結束子程序的資源。

waitpid

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

引數
pid:

  1. pid > 0:等待其程序ID與pid相等的子程序
  2. pid = 0:等待同一個程序組中的任何子程序,如果子程序已經加入了別的程序組,waitpid 不會等待它。
  3. pid= -1:等待任意一個子程序。c此時與wait等效
  4. pid < -1:等待指定程序組中的任何子程序,這個程序組的 ID 等於 pid 的絕對值。

options:
0同 wait(),阻塞父程序,等待子程序退出。
WNOHANG: 若pid指定的子程序沒有結束,則 waitpid()函式立即返回0,不予以等待。若程序正常結束,則返回該子程序的ID。
WUNTRACED:如果子程序暫停了則此函式馬上返回,並且不予以理會子程序的結束狀態。(很好用,知道有這個東西即可)

返回值:
1.當正常返回的時候waitpid返回收集到的子程序的程序ID;
2. 如果設定了選項 WNOHANG,而呼叫中 waitpid發現沒有已退出的子程序可收集,則返回0;
3. 如果呼叫中出錯,則返回-1,這時 errno會被設定成相應的值以指示錯誤所在;(如:當 pid 所對應的子程序不存在,或此程序存在,但不是呼叫當前程序的子程序,waitpid() 就會出錯返回,這時 errno 被設定為 ECHILD)

statues:
WIFEXITED( status): 若為正常終止子程序返回的狀態,則為真。(檢視程序是否是正常退出)
WEXITSTATUS(sauS): 若 WIFEXITED非零,提取子程序退出碼。(檢視程序的退出碼)

status: 程序退出時的狀態資訊。

1.如果引數 status 的值不是 NULL,wait() 就會把子程序退出時的狀態取出並存入其中,這是一個整數值(int),指出了子程序是正常退出還是被非正常結束的。
2.在wait的引數中儲存了子程序的推出原因以及退出碼,雖然退出狀態用了四個位元組儲存,而引數中只用了低16位兩個位元組用於儲存這些資訊。在這低16位中,高8位儲存的是子程序的退出碼,只有子程序執行完畢退出時才會有,低8位為0。低7位儲存子程序引起異常退出的訊號值,第8位儲存core dump,只有子程序異常退出時才會有,高8位為0。
status & 0x7f :判斷是否正常退出,並獲取退出訊號
(status >> 8)& 0xff :獲取正常退出碼
status & 0x7f:獲取異常退出的終止訊號值

在這裡插入圖片描述

另外,這個退出資訊在一個 int 中包含了多個欄位,直接使用這個值是沒有意義的,我們需要用巨集定義取出其中的每個欄位。

下面我們來介紹其中最常用的兩個巨集定義,取出子程序的退出資訊:
WIFEXITED(status): 如果子程序是正常終止的,取出的欄位值非零。

WEXITSTATUS(status): 返回子程序的退出狀態,退出狀態儲存在 status 變數的 8~16 位。在用此巨集前應先用巨集 WIFEXITED 判斷子程序是否正常退出,正常退出才可以使用此巨集。
也就是說,這兩個巨集就對應上面的前兩種方法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main(int argc, char *argv[])
{
	pid_t pid;
	
	pid = fork(); // 建立程序
	if( pid < 0 ){ // 出錯
		perror("fork");
		exit(0);
	}
	
	if( pid == 0 ){// 子程序
		int i = 0;
		for(i=0;i<5;i++)
		{
			printf("this is son process\n");
			sleep(1);
		}
		
		_exit(2); // 子程序退出,數字 2 為子程序退出的狀態
		
	}else if( pid > 0){ // 父程序
		
		int status = 0;
		
		// 等待子程序結束,回收子程序的資源
		// 此函式會阻塞
		// status 某個欄位儲存子程序呼叫 _exit(2) 的 2,需要用巨集定義取出
		wait(&status); 
		// waitpid(-1, &status, 0); // 和 wait() 沒區別,0:阻塞
		// waitpid(pid, &status, 0); // 指定等待程序號為 pid 的子程序, 0 阻塞
		// waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞
		if(WIFEXITED(status) != 0){ // 子程序是否正常終止
			printf("son process return %d\n", WEXITSTATUS(status));
		}
		
		printf("this is father process\n");	
	}
	
	return 0;
}

在這裡插入圖片描述