天天看點

程式的終止及atexit函數詳解

1、背景

對C語言有所了解的人,都知道main函數是整個程式的入口。

但是其實不是,在核心中可以使用連結器來設定程式的開始地方。如下:

當核心使⽤⼀個exec函數執⾏C程式時,在調⽤main函數之前先調⽤⼀個特殊的啟動例程。可執⾏程式檔案将此啟動例程指定為程式的起始位址(這是由連接配接編輯器設定的,而連接配接編輯器則由C編輯器調用)。

啟動例程從核心擷取指令⾏參數和環境變量,然後為調⽤main函數做好準備。

2、atexit

前面我們關注的是程式開始進入時的調用函數,而atexit函數是一個特殊的函數,它是在正常程式退出時調用的函數,我們把他叫為登記函數(函數原型:

int atexit (void (*)(void))

程式的終止及atexit函數詳解

  參數:函數位址

傳回值:成功傳回0,出錯傳回非0

根據ISO C的規定,⼀個程序可以登記多至32個函數,這些函數由exit⾃動調⽤,這些函數被稱為終⽌處理程式,

atexit函數可以登記這些函數。

值得說明的是:exit調⽤終⽌處理函數的順序和atexit登記的順序相反,如果⼀個函數被多次登記,也會被多次調⽤。

3、程式的終止

程序終⽌的⽅式有8種,前5種為正常終⽌,後三種為異常終⽌:

1 從main函數傳回;

2 調⽤exit函數;

3 調⽤_exit或_Exit;

4 最後⼀個線程從啟動例程傳回;

5 最後⼀個線程調⽤pthread_exit;

6 調⽤abort函數;

7 接到⼀個信号并終⽌;

8 最後⼀個線程對取消請求做出響應。

exit()和_exit()以及_Exit()函數的本質差別是是否立即進入核心,_exit()以及_Exit()函數都是在調用後立即進入核心,而不會執行一些清理處理,但是exit()則會執行一些清理處理。

這也是為什麼會存在atexit()函數的原因,因為exit()函數需要執行清理處理,需要執行一系列的操作,這些終止處理程式實際上就是完成各種所謂的清除操作的實際執行體。

我用一個圖簡單表示了其過程:

程式的終止及atexit函數詳解

注意:核心使程式執行的唯一方法是調用一個exec函數。

程序自願終止的唯一方法是顯示或隐式的(調用exit)調用_exit或_Exti。

程序也可非自願地由一個信号使其終止。

4、atexit函數的使用

代碼如下:

#include<stdio.h>
#include<stdlib.h>

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

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

int main(){
    if(atexit(my_exit1)!=0){
        printf("can't register my_exit1");
    }

   if(atexit(my_exit1)!=0){
        printf("can't register my_exit1");
    }

    if(atexit(my_exit2)!=0){
        printf("can't register my_exit2");
    }
    return 0;
}      

結果: