1. 程式人生 > >Unix程式設計——程序

Unix程式設計——程序

exit函式

終止方式分為兩種:

  • 正常終止:
    • main函式中執行return
    • 呼叫exit函式,該函式會關閉所有標準I/O
    • 呼叫_exit系統呼叫函式,此函式由exit函式呼叫
  • 異常終止
    • 呼叫abort,它產生一個SIGABRT訊號
    • 當程序接收到某個訊號時

不管程序如何終止,最後都會執行同一行程式碼,這段程式碼會為相應程序關閉所有開啟的描述符,釋放它所使用的儲存器

程序為了通知它的父程序自己是如何終止的,在終止的時候會返回一個退出狀態終止狀態,父程序可以使用waitwaitpid函式來獲得其子程序的終止狀態

退出狀態終止狀態是有區別的: 在最後呼叫_exit時核心將其退出狀態轉換成終止狀態 一個C

程式是如何啟動和終止的

核心使程式執行的唯一方法是呼叫一個exec函式 程序自願終止的唯一方式時呼叫一個_exit(exit)函式

atexit函式

從入中可以看到,程序在呼叫exit函式時,exit函式又呼叫了一系列 終止處理程,這些終止處理程式是由atexit函式負責註冊的,ANSI C規定一個程序至多註冊32個函式,這些函式由exit自動呼叫

atexit函式: #include <stdlib.h> int atexit(void (*func)(void)) ;

該函式的引數是一個函式地址,作為引數的那個函式地址所指向的函式無參且無返回值atexit成功返回0,否則為1

atexit函式使用示例:

#include "ourhdr.h"
#include "my_err.h"

static void my_exit1(void);
static void my_exit2(void);

int main(void)	
{
	if(atexit(my_exit2) != 0)
		err_sys("can't register my_exit2");
	
	if(atexit(my_exit1) != 0)
		err_sys("can't register my_exit1");
	
	printf("main is done\n");
	return 0;
}

static void my_exit1(void)
{
	printf("first exit handler\n");
}

static void my_exit2(void)
{
	printf("second exit handler\n");
}

現在回來接著說exit函式,上面說父程序接收子程序的退出狀態,但是如果父程序先於子程序結束呢?

  • 在這種情況下,子程序的父程序會自動改變為init程序,它的PID為1

UNIX中,一個已經終止,但是其父程序卻沒有給它收屍的程序,被稱作殭屍程序(Zombie),這裡說的沒有收屍,意思就是父程序沒有呼叫waitwaitpid函式來對子程序進行回收操作,回收操作具體為:

獲取終止子程序的有關資訊、釋放它仍佔用的資源

wait、waitpid函式

現在我們來說一下waitwaitpid函式

當程序終止時(不管是正常終止還是異常終止),核心都會向其父程序傳送SIGCHLD訊號 因為子程序終止是個非同步事件(這可以在父程序執行的任何時候發生 ),所以這種訊號也是核心向父程序發的非同步通知

對於該訊號,父程序有兩種處理選項

  • 不處理,忽略訊號
  • 為該訊號提供一個處理函式

呼叫wait或者waitpid的程序會出現以下這三種狀況:

  • 阻塞(如果其所有子程序都還在執行)
  • 帶著子程序的終止狀態立即返回(如果一個子程序已終止,正等待父程序存取其終止狀態)
  • 出錯立即返回(如果它沒有任何子程序)

如果我們隨意呼叫wait函式,很可能會導致程序阻塞,因為此時並不一定有子程序結束,也就是說沒有SIGCHLD訊號發出

wait函式和waitpid函式的區別

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
										兩個函式返回:若成功則為程序ID,若出錯則為-1
  • 在一個子程序終止前,wait 使其呼叫者阻塞,而waitpid有一選擇項,可使呼叫者不阻塞
  • waitpid並不等待第一個終止的子程序—它有若干個選擇項,可以控制它所等待的程序

它倆有一個共同的引數statloc,一個int指標,它所指向的記憶體單元是用來存放子程序的終止狀態的,如果不關心子程序的終止狀態,則設為null即可 在UNIX中,使用以下三個巨集來獲取終止狀態,他們定義在<sys/wait.h>標頭檔案中,均以WIF開頭

巨集 說 明
WIFEXITED(status) 若為正常終止子程序返回的狀態,則為真。對於這種情況可執行WEXITSTATUS(status)取子程序傳送給exit_exit引數的低8
WIFSIGNALED(status) 若為異常終止子程序返回的狀態,則為真(接到一個不捕捉的訊號)。對於這種情況,可執行WTERMSIG(status)取使子程序終止的訊號編號。另外,SVR4和4.3+ BSD(但是,非POSIX.1)定義巨集:WCOREDUMP(status),若已產生終止程序的core檔案,則它返回真
WIFSTOPPED(status) 若為當前暫停子程序的返回的狀態,則為真。對於這種情況,可執行WSTOPSIG(status)取使子程序暫停的訊號編號

下面是關於wait函式和這三個巨集的示例程式:

#include <sys/wait.h>
#include "ourhdr.h"
#include "my_err.h"
void pr_exit(int status) 
{
	if(WIFEXITED(status))
		printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
	else if(WIFSIGNALED(status))
		printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
		WCOREDUMP(status) ? " (core file.generated)" : "");
#else 
		"");
#endif
	else if(WIFSTOPPED(status))
		printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}

int main(void)
{
	pid_t pid;
	int status;
	
	if((pid =fork()) < 0)
		err_sys("fork error");
	else if(pid == 0)
		exit(7);
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
		
		if((pid = fork) < 0)
		err_sys("fork error");
	else if(pid == 0)
		abort();
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
	
	if((pid = fork()) < 0)
		err_sys("fork error");
	else if(pid == 0)
		status /= 0;
	
	if(wait(&status) != pid)
		err_sys("wait error");
	pr_exit(status);
	
	exit(0);
}