進程的啓動和終止

C程序的啓動過程

        我們通常認爲C語言的起始函數是main函數,實際上一個程序的啓動函數不一定是main函數,這個可以採用鏈接器來設置,但是gcc中默認main函數就是C語言的入口函數,在執行main函數之前,內核會啓動一個特殊例程

        這個特殊例程的作用:

  • 蒐集命令行的參數傳遞給main函數中的argc和argv;
  • 蒐集環境信息構建環境表並傳遞給main函數;
  • 登記進程的終止函數
        因此,對於程序來說,main函數並不是起始的,但是對C語言而言,main函數就是入口地址,其他的鏈接器幫助我們完成,實際上main函數的執行時使用了exec函數,這是一個函數族,這也是一個內核執行一個程序的唯一方法,這在進程控制部分將進行分析。


進程終止函數atexit()

        #include <stdlib.h>

        int atexit(void(*function)(void));

        返回:成功返回0,出錯返回-1;

        功能:註冊終止函數(即main執行結束後調用的函數,當程序通過調用exit()或從main中返回時,參數function指定的函數會先被                     調用,然後才真正由exit()結束程序)

  • 每個啓動的進程都默認登記了一個標準的終止函數;
  • 終止函數在進程終止時釋放進程所佔用的一些資源;
  • 登記的多個終止函數執行順序是以棧的方式執行,先登記的後執行;
        按照ISO C的規定,一個進程可以登記多達32個函數,這些函數將由exit自動調用。atexit()註冊的函數類型應爲不接收任何參數的void函數,exit調用這些註冊函數的順序與他們登記時候的順序相反。同一個函數如若多次登記,則也會被多次調用。


進程終止方式

    a.正常終止

  • 從main函數返回;
  • 調用exit(標準c庫函數);
  • 調用_exit或_Exit(系統調用)
    b.異常終止

  • 調用abort;
  • 接收到一個信號並終止;
  • 最後一個線程對其取消請求做處理響應;
    c.進程返回

  • 通常程序運行成功返回0,失敗返回非0;
  • 在shell中可以查看進程返回值(echo $?);


一個問題:main函數退出之後,是否還可以執行程序?

        #include 

        int atexit(void(*func)(void));

        其中,atexit的參數是一個地址,當調用此函數時無需傳遞任何參數,該函數也無返回值,atexit函數稱爲終止處理程序註冊程序,註冊完成以後,當函數終止時,exit()函數會主動調用前面的各個函數。由於exit是在main函數調用結束以後調用,所以這些函數的執行肯定在main函數之後。這就是上面問題的答案。即採用atexit函數登記相關的執行函數即可。

        簡單的示例:

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

void func1(void)
{
      printf("in func1\n");
}
void func2(void)
{
      printf("in func2\n");
}
void func3(void)
{
      printf("in func3\n");
}

int main(void)
{
      atexit(func1);
      atexit(func2);
      atexit(func3);

      printf("I'm main\n");
      
      exit(0);
      //return 0;
}
具體執行結果如下所示:



        根據exit的執行過程可知,exit首先會調用各個終止處理程序,然後按需多次調用close(),關閉所有打開流,也就是說exit函數會執行一個標準庫的清理關閉操作,對所有打開的流調用fclose(),這樣就會造成所有緩衝的輸出數據都被沖洗寫入文件中。


        注意:在終止方式方式中,調用_exit,_Exit都不會調用終止程序,異常終止也不會。

        如果將上述程序中最後的exit(0)改爲_exit(0)或_Exit(0),執行結果如下:


        也就是說根本不會執行到最開始註冊的函數。

發佈了70 篇原創文章 · 獲贊 29 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章