一步步编写操作系统 46 用c语言编写内核3
再把上節代碼貼出來,
1 //int main(void) { 2 int _start(void) { 3 while(1); 4 return 0; 5 }有沒有同學想過,這里寫一個_start函數,讓其調用main函數如何?其實這是可以的,main函數并不是第一個函數,它實際上也是被別人調用的,不過這是編譯器背后的策略啦,好奇心大的同學自己嘗試下吧。
雖然把函數名改成_start可以解決問題,但我們習慣于main函數做為主函數,不習慣函數用_start,于是用了-e來指定起始的函數名為main,所以代碼才鏈接正常。
也許有同學想過,哎?我平時寫的程序也沒有_start啊,直接用gcc編譯后就能運行,沒出過問題啊。是啊,確實如您所說,由于我也沒深入研究過,但咱們通過比較的方式,讓您自己悟出這里面的秘密。還是用上述代碼為例,gcc –o /tmp/test.bin kernel/main.c編譯鏈接,由于未加-c參數,生成的test.bin不再是目標文件而是可執行文件。然后再用先編譯成目標文件再鏈接成可執行文件的方式,對比這兩個文件的區別。見圖
您看,test.bin是gcc直接生成的可執行文件,它的大小是4586字節。而kernel.bin是經過手動編譯、鏈接這兩個步驟完成的,其文件大小是1777字節。這兩個文件的體積可是差了幾乎2倍呢。再看看這兩個文件中的符號信息,還是用nm命令,如圖
test.bin中共有34個符號(wc –l命令是用來統計輸出的行數,一個符號占用一行,故34個符號),由于輸出太長了,我們只截取了關鍵的部分,不過您看那些frame_dummy、data_start等,這并不是咱們代碼中存在的符號,這說明在編譯器在編譯過程中為咱們引用了別的代碼,這就是c運行庫的功勞,目的是在調用main函數前做初始化環境等工作。您看,用白色方框圈出來的_start,這就是默認的入口符號,鏈接器還是用到了它,它不是咱們提供的代碼,依然是運行庫提供的,這也說明main函數不是第一個執行的代碼,它一定是被其它代碼調用的,main函數在運行庫代碼初始化完環境后才被調用。
咱們繼續看kernel.bin中的符號,一共就4行,盡管其中也包含了咱們不認識的符號,但畢竟少得多,我們的程序更短小精干,而且確實沒有_start函數。這里添加了3個類型為A的符號,這表示它們的值是不變的。T表示是該符號是位于代碼段中,更多符號的意義請參考man nm。
其實上述代碼中要是換成匯編代碼的話,就是個jmp $,其大小不過是2字節的機器碼ebfe。除了編譯器自動添加的代碼外,一般情況下c語言編譯出來的程序也比匯編語言生成的程序體積大。可見,人們常說的匯編語言比c語言快,并不是匯編語言本身有多快(它也要變成機器指令后才能上cpu運行),而是匯編語言對應的機器指令是一對一,簡單直接可依賴,而c語言生成的機器指令是一對多,復雜間接略冗余。
好啦,關于內核的部分咱們就此先打住,其實說這話我有點不好意思,您也看到啦,內核代碼中就一個死循環而已,我們的內核還沒有開始。咱們的內核雖然離真正的內核差得十萬八千里,但它目的是兩個:
后面我們將結合此簡單至極的c程序來學習有關elf方面的知識。
總結
以上是生活随笔為你收集整理的一步步编写操作系统 46 用c语言编写内核3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 又一款高收益稳健理财要消失!想要收益率超
- 下一篇: 一步步编写操作系统 17 显存,显卡,显