Unix程式設計——程序
exit函式
終止方式分為兩種:
- 正常終止:
- 在
main
函式中執行return
- 呼叫
exit
函式,該函式會關閉所有標準I/O
流 - 呼叫
_exit
系統呼叫函式,此函式由exit
函式呼叫
- 在
- 異常終止
- 呼叫
abort
,它產生一個SIGABRT
訊號 - 當程序接收到某個訊號時
- 呼叫
不管程序如何終止,最後都會執行同一行程式碼,這段程式碼會為相應程序關閉所有開啟的描述符,釋放它所使用的儲存器
程序為了通知它的父程序自己是如何終止的,在終止的時候會返回一個退出狀態或終止狀態,父程序可以使用wait
或waitpid
函式來獲得其子程序的終止狀態
退出狀態和終止狀態是有區別的:
在最後呼叫_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),這裡說的沒有收屍,意思就是父程序沒有呼叫wait
或waitpid
函式來對子程序進行回收操作,回收操作具體為:
獲取終止子程序的有關資訊、釋放它仍佔用的資源
wait、waitpid函式
現在我們來說一下wait
和waitpid
函式
當程序終止時(不管是正常終止還是異常終止),核心都會向其父程序傳送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);
}