一、 前言 本章將學習:當執行程式時,其main函數是如何被調用的,命令行參數是如何傳送給執行程式的,典型的存儲器佈局是什麼樣式,如何分配另外的存儲空間,進程如何使用環境變數,進程終止的不同方式等。另外還將說明longjmp和setjmp函數以及它們與棧的交互作用。 二、 main函數 C程式的入口 ...
一、 前言
本章將學習:當執行程式時,其main函數是如何被調用的,命令行參數是如何傳送給執行程式的,典型的存儲器佈局是什麼樣式,如何分配另外的存儲空間,進程如何使用環境變數,進程終止的不同方式等。另外還將說明longjmp和setjmp函數以及它們與棧的交互作用。
二、 main函數
C程式的入口是main函數,main函數的原型是:
int main(int argc, char argv[]);
當內核啟動C程式時(使用一個exec函數),在調用main前先調用一個特殊的啟動常式。可執行程式文件將此啟動常式制定為程式的起始地址--這是由鏈接器設置的,而鏈接器程式則由C編譯程式(通常是cc)調用。啟動常式從內核取得命令行參數和環境變數值,然後為調用main函數作好安排。
三、進程終止
有五種方式使進程終止:
-
正常終止: (a) 從main返回。
(b) 調用exit。
(c) 調用_exit。 -
異常終止: (a) 調用abort。
(b) 由一個信號終止。
上節提到的啟動常式是這樣編寫的,使得從main返回後立即調用exit函數,如果將啟動歷程以C代碼形式表示(此常式通常用彙編編寫),則它調用main函數的形式可能是: exit(main(argc, argv));
1. exit和_exit函數
exit和_exit函數用於正常終止一個程式:_exit立即進入內核,exit則先執行一些清除處理(包括調用執行各終止處理程式,關閉所有標準IO流等),然後進入內核。
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int statu);
使用不同頭文件的原因是:exit是由ANSI C說明的,而_exit則是由POSIX.1說明的
由於歷史原因,exit函數總是執行一個標準IO庫的清除關閉操作:對所有打開的流調用fclose函數,這會造成緩存中的所有數據都被刷新(寫入到文件上)。
2. atexit函數
按照ANSI C的規定,一個進程可以登記多至32個的終止處理函數(exit handler),這些函數將由exit自動調用。可用atexit函數來註冊這些函數。
#include <stdlib.h>
int atexit(void (*func)(void));
返回值:成功為0,出錯非0
atexit的參數是一個無參數並且無返回的函數的指針。exit以註冊這些函數的相反順序調用它們。如果一個函數被註冊多次那會被調用多次。 根據ANSI C和POSIX.1 exit首先調用各終止處理程式,然後按需多次調用fclose。下圖顯示了一個C程式是如何啟動的,以及它終止的各種方式。
內核使程式執行的唯一方式是調用一個exec函數。
四、命令行參數
當執行一個程式時,調用exec的進程可將命令行參數傳給該程式。
五、環境表
每個程式都接收到一張環境表。與參數表一樣,環境表也是一個字元指針數組,其中每個指針包含一個以null結束的字元串的地址。全局變數environ則包含了該指針數組的地址。 extern char **environ;
如果該環境包含五個字元串,那麼它們看起來如下圖:
其中每個字元串的結束處都有一個null字元。我們稱environ為環境指針。指針數組為環境表,其中各指針指向的字元串為環境字元串。通常使用getenv和putenv函數來存取特定的環境變數,而不是用environ變數。但是如果要查看整個環境則必須使用environ指針。
六、C程式的存儲空間佈局
C程式由以下幾部分組成:
- 正文段。這是由CPU執行的機器指令部分。通常正文段是共用的,所以即使是經常執行的程式(如文本編輯程式、C編譯程式、shell等)在存儲器中也只需有一個副本。另外正文段常常是只讀的。
- 初始化數據段。通常將此段稱為數據段。其中包含了程式中需賦初值的變數。如C程式中任何函數之外的說明:
使此變數以初值存放在初始化數據段中。int maxcount = 99;
- 非初始化數據段。通常稱此段為bss段(block started by symbol, 由符號開始的塊),在程式開始執行之前,內核將此段初始化為0。函數外的說明:
long sum[1000]
使此變數存放在非初始化數據段中。 - 棧。自動變數以及每次函數調用所需保存的信息(返回地址和調用者的環境信息)都存放在此段中。
- 堆。通常在堆中進行動態存儲分配。堆位於非初始化數據段項和棧底之間。
下圖顯示了這些段的典型安排方式。
從圖中可以看到未初始化數據段的內容並不存放在磁碟程式文件中。需要存放在磁碟程式文件中的段只有正文段和初始化數據段。size命令報告正文段、數據段、和bss段的長度:
$ size /bin/cc /bin/sh
text data bss dec hex
81920 16384 664 98968 18298 /bin/cc
90112 16384 0 106496 1a000 /bin/sh
第4列和第5列分別以十進位和十六進位表示的總長度。