main函數執行以前

現在最重要的是要跟得上潮流,所以套用比較時髦的話,誰動了我的奶酪。誰調用了我的 main?不過作爲計算機工作者,我勸大家還是不要趕時髦,今天Java熱,明天 .net 流行,什麼時髦就學什麼。我的意思是先花幾年把基本功學好,等你趕時髦的時候也好事半功倍。廢話不多說了。
    
    我們都聽說過一句話:“main是C語言的入口”。我至今不明白爲什麼這麼說。就好像如果有人說:“掙錢是泡妞”,肯定無數磚頭拍過來。這句話應該是“掙錢是泡妞的一個條件,只不過這個條件特別重要”。那麼上面那句話應該是 “main是C語言中一個符號,只不過這個符號比較特別。”
    
    我們看下面的例子:
    
    /* file name test00.c */
    
    int main(int argc, char* argv)
    {
     return 0;
    }
    
    編譯鏈接它:
    cc test00.c -o test.exe
    會生成 test.exe
    
    但是我們加上這個選項: -nostdlib (不鏈接標準庫)
    cc test00.c -nostdlib -o test.exe
    鏈接器會報錯:
    undefined symbol: __start
    
    也就是說:
    1. 編譯器缺省是找 __start 符號,而不是 main
    2. __start 這個符號是程序的起始點
    3. main 是被標準庫調用的一個符號
    
    再來思考一個問題:
    我們寫程序,比如一個模塊,通常要有 initialize 和 de-initialize,但是我們寫 C 程序的時候爲什麼有些模塊沒有這兩個過程麼呢?比如我們程序從 main 開始就可以 malloc,free,但是我們在 main 裏面卻沒有初始化堆。再比如在 main 裏面可以直接 printf,可是我們並沒有打開標準輸出文件啊。(不知道什麼是 stdin,stdout,stderr 以及 printf 和 stdout 關係的羣衆請先看看 C 語言中文件的概念)。
    
    有人說,這些東西不需要初始化。如果您真得這麼想,請您不要再往下看了,我個人認爲計算機軟件不適合您。
    
    聰明的人民羣衆會想,一定是在 main 之前幹了些什麼。使這些函數可以直接調用而不用初始化。通常,我們會在編譯器的環境中找到一個名字類似於 crt0.o 的文件,這個文件中包含了我們剛纔所說的 __start 符號。(crt 大概是 C Runtime 的縮寫,請大家幫助確認一下。)
    
    那麼真正的 crt0.s 是什麼樣子呢?下面我們給出部分僞代碼:
    
    ///////////////////////////////////////////////////////
    section .text:
    __start:
    
     :
     init stack;
     init heap;
     open stdin;
     open stdout;
     open stderr;
     :
     push argv;
     push argc;
     call _main; (調用 main)
     :
     destory heap;
     close stdin;
     close stdout;
     close stderr;
     :
     call __exit;
    ////////////////////////////////////////////////////
    
    實際上可能還有很多初始化工作,因爲都是和操作系統相關的,筆者就不一一列出了。
    
    注意:
    1. 不同的編譯器,不一定缺省得符號都是 __start。
    2. 彙編裏面的 _main 就是 C 語言裏面的 main,是因爲彙編器和C編譯器對符號的命名有差異(通常是差一個下劃線'_')。
    3. 目前操作系統結構有兩個主要的分支:微內核和宏內核。微內核的優點是,結構清晰,簡單,內核組件較少,便於維護;缺點是,進程間通信較多,程序頻繁進出內核,效率較低。宏內核正好相反。我說這個是什麼目的是:沒辦法保證每個組件都在用戶空間(標準庫函數)中初始化,有些組件確實可能不要初始化,操作系統在創建進程的時候在內核空間做的。這依賴於操作系統的具體實現,比如堆,宏內核結構可能在內核初始化,微內核結構在用戶空間;即使同樣是微內核,這個東東也可能會被拿到內核空間初始化。
    
    隨着 CPU 技術的發展,存儲量的迅速擴展,代碼複雜程度的增加,微內核被越來越多的採用。你會爲了 10% 的效率使代碼複雜度增加麼?要知道每隔 18 個月 CPU 的速度就會翻一番。所以我對程序員的要求是,我首先不要你的代碼效率高,我首先要你的代碼能讓 80% 的人迅速看懂並可以維護。

總結:

main函數執行之前,主要就是初始化系統相關資源:

1.設置棧指針

2.初始化static靜態和global全局變量,即data段的內容

3.將未初始化部分的賦初值:數值型short,int,long等爲0,bool爲FALSE,指針爲NULL,等等,即.bss段的內容

4.運行全局構造器,估計是C++中構造函數之類的吧

5.將main函數的參數,argc,argv等傳遞給main函數,然後才真正運行main函數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章