之前總結了Linux的系統創建,主要是fork()函數和vfork()函數,最近總結了Linux進程的終止,主要的調用是_exit()和exit().
先看看兩個函數的原型以及各自屬於的頭文件,可以發現這兩個方法的區別
_exit()函數:
#include <unistd.h> void _exit(int status);
從_exit()的頭文件能夠發現,_exit()是屬於Linux的系統調用, 只能在Linux或者是Unix上才支持這個調用。
其中,status定了進程的終止狀態,其父進程可以通過wait()函數來獲取這個狀態,從而進行之后的執行操作。當status為0時,表示進程正常退出,反之則是非正常退出。調用_exit()會使進程總是成功終止,也就是說_exit()方法從不會返回。
此處需要注意的是,雖然status定義成了整型,但實際上只有低8位可用,其原因就是當以信號(signal)終止一個進程的時候,shell會將變量$?置為128與信號之和,以表征這一事實。如果這與進程調用_exit()時所使用相同status值混雜起來,將令shell無法分辨。(這個地方不是很理解)
exit()函數:
#include <stdlib.h> void exit(int status);
從exit()的頭文件可以看出,exit()是C語言的庫函數,所以所有的C語言都支持這個方法。
exit()在調用的時候要做得工作會比較多,主要有如下幾個操作:
- 調用退出處理程序(通過atexit()和on_exit()注冊的函數),並且在有多個退出處理程序時,執行順序與注冊順序相反。
- 刷新stdio流緩沖區;
- 使用有status提供的值執行_exit()函數
我們通常在寫程序的時候,在main()函數結尾只是用了return n;來結尾,在這個地方,return n; 就相當於exit(n),在結束的時候主函數會將返回值作為調用exit()函數的參數。這個地方本人有個疑問,在exit()函數調用_exit()時,其參數使用前面的n,而在_exit(int status)中,status為0表示程序正常結束,如果在return的時候,指定的n不為0,是一個其他的數,Linux內部會怎么處理呢?
其實在進程終止的時候,還有一些更細致的操作,由於本人目前能力有限,先不做討論。
剛剛在前面提到了退出處理程序,現在來看看退出處理程序。
使用退出處理程序的原因就是,我們調用的exit()是C語言的庫函數,而庫函數對於進程的退出並沒有實際控制的權利,所以無法在進程退出時調用系統特定的清理函數,所以退出處理函數就應運而生。
GUNC 提供了一下兩種方式來注冊退出處理函數:
#include <stdlib.h> int atexit(void (*func)(void)); //退出處理函數的定義 void func(void) { /* performance */ }
需要注意的是:
- atexit()函數在出錯的時候返回的是非0值,不僅僅是負值;
- 同時,在退出處理函數中如果訪問了此前mian()函數中本地變量,那么main()函數的返回會導致未定義的行為;
- 當有多個退出處理程序的時候,退出處理程序的調用順序與之策順序是相反的,這一點的邏輯是,先注冊的通常是更為基本的清理動作,可能需要在后續注冊的函數后再執行;
- 一旦退出處理程序在無法返回——調用了_exit()或者因為信號而終止,其后的處理程序將不再執行。
以上的atexit()有兩個局限,第一是退出處理程序不能獲取當前進程退出時的狀態,而根據進程退出狀態來做相應的操作可以是支持的;第二就是不能給退出處理程序傳遞參數。
所以glibc提供了一個非標准的替代方法:on_exit()。其定義如下:
#define _BSD_SOURCE #include <stdlib.h> int on_exit(void (*func)(int, void *),void *arg); //退出處理函數定義 void func(int status, void *arg) { /* Performace */ }
和atexit()函數類似,on_exit()的出錯時返回值為非0。而on_exit()還不是所有標准都支持,還是應該盡量避免。
atexit()和on_exit()注冊的函數屬於同一個函數列表,在執行時與注冊的順序相反。
最后給一個例子僅供大家參考:
#define _BSD_SOURCE #include <stdlib.h> #include <sys/types.h> #include <stdio.h> static void atexitFunc1(void) { printf("atexit function 1 called\n"); } static void atexitFunc2(void) { printf("atexit function 2 called\n"); } static void onexitFunc(int exitStatus, void *arg) { printf("on_exit function called: status=%d, arg=%ld\n",exitStatus, (long) arg); } int main(int argc, char *argv[]) { if(on_exit(onexitFunc, (void *) 10) != 0) perror("on_exit 1"); if(atexit(atexitFunc1) != 0) perror("atexit 1"); if(atexit(atexitFunc2) != 0) perror("atexit 2"); if(on_exit(onexitFunc, (void *) 10) != 0) perror("on_exit 2"); exit(2); }
執行的結果如下:
on_exit function called: status=2, arg=20 atexit function 2 called atexit function 1 called on_exit function called: status=2, arg=10
希望和大家多交流