atexit函數和exit函數的理解
按照ISO C的規定,一個(gè)進(jìn)程可以登記多達32個(gè)函數,這些函數將由exit自動(dòng)調用,通常這32個(gè)函數被稱(chēng)為終止處理程序,并調用atexit函數來(lái)登記這些函數。
本文引用地址:http://dyxdggzs.com/article/201612/324502.htm 我們通常認為C語(yǔ)言的起始函數是main函數,實(shí)質(zhì)上一個(gè)程序的啟動(dòng)函數并不一定是main函數,這個(gè)可以采用鏈接器來(lái)設置,但是gcc中默認main就是C語(yǔ)言的入口函數,在main函數啟動(dòng)之前,內核會(huì )調用一個(gè)特殊的啟動(dòng)例程,這個(gè)啟動(dòng)例程從內核中取得命令行參數值和環(huán)境變量值,為調用main函數做好準備,因此對應程序而言main函數并不是起始,但是對應C 語(yǔ)言而言,main函數就是入口地址,其他的鏈接器幫助我們完成,實(shí)際上mian函數的執行是使用了exec函數,這是一個(gè)函數族,這也是內核執行一個(gè)程序的唯一方法,這在進(jìn)程控制部分將進(jìn)行分析。
記得在面試題中有一道關(guān)于在main函數退出之后,是否還可以執行程序的問(wèn)題,這時(shí)候就要使用到前面提到的atexit函數。
#include
int atexit(void(*func)(void));
其中,atexit的參數是一個(gè)函數地址,當調用此函數時(shí)無(wú)須傳遞任何參數,該函數也不能返回值,atexit函數稱(chēng)為終止處理程序注冊程序,注冊完成以后,當函數終止是exit()函數會(huì )主動(dòng)的調用前面注冊的各個(gè)函數,但是exit函數調用這些函數的順序于這些函數登記的順序是相反的,我認為這實(shí)質(zhì)上是參數壓棧造成的,參數由于壓棧順序而先入后出。同時(shí)如果一個(gè)函數被多次登記,那么該函數也將多次的執行。
我們知道exit是在main函數調用結束以后調用,因此這些函數的執行肯定在main函數之后,這也是上面面試題的解決方法。即采用atexit函數登記相關(guān)的執行函數即可。
在exit函數的介紹中我們知道,exit()和_exit()以及_Exit()函數的本質(zhì)區別是是否立即進(jìn)入內核,_exit()以及_Exit()函數都是在調用后立即進(jìn)入內核,而不會(huì )執行一些清理處理,但是exit()則會(huì )執行一些清理處理,這也是為什么會(huì )存在atexit()函數的原因,因為exit()函數需要執行清理處理,需要執行一系列的操作,這些終止處理函數實(shí)際上就是完成各種所謂的清除操作的實(shí)際執行體。atexit函數的定義也給了程序員一種運用exit執行一些清除操作的方法,比如有一些程序需要額外的操作,具體的清除操作可以采用這種方法對特殊操作進(jìn)行清除等。
#include
#include
void func1(void)
{
printf("in func1");
}
void func2(void)
{
printf("in func2");
}
void func3(void)
{
printf("in func3");
}
int main()
{
atexit(func3);
atexit(func2);
atexit(func1);
printf("In main");
return 0;
}
具體的執行結果如下所示:
[gong@Gong-Computer APUE]$ ./atexit
In main
in func1
in func2
in func3
根據exit的執行過(guò)此可知,exit首先會(huì )調用各個(gè)終止處理程序,然后按需多次調用fclose(),關(guān)閉所有打開(kāi)流,也就是說(shuō)exit函數會(huì )執行一個(gè)標準I/O庫的清理關(guān)閉操作:對所有打開(kāi)的流調用fclose(),這樣就會(huì )造成所有緩沖的輸出數據都被沖洗即寫(xiě)入文件中。
內核使程序執行的唯一方法是調用一個(gè)exec函數,進(jìn)程自愿終止哦的唯一方法是顯式或者隱式調用(通過(guò)exit函數)_exit()或者_Exit()函數。因此exit函數中實(shí)質(zhì)是對_exit()或者_Exit()函數的封裝。exit會(huì )先執行自定義的終止處理函數,然后執行I/O庫函數清理函數fclose(),這也是為什么可以在終止處理函數中可以繼續運用printf之類(lèi)函數的原因,因為I/O庫函數的流對象還沒(méi)有被清除,當然可以繼續運用。執行完了所有的fclose()以后,可以執行真正意義上的終止函數_exit()或者_Exit()函數。
評論