一步步编写操作系统 44 用c语言编写内核1
先來個簡單的,歡迎我們神秘嘉賓——main.c。這是我們第一個c語言代碼。
1 int main(void) { 2 while(1); 3 return 0; 4 }它沒法再簡單啦,簡單的程序似乎能幫助咱們更容易的理解所學的知識,哈哈,我說的是似乎,其實,再長的代碼,編譯后生成的文件結構也是由那幾個部分組成,萬變不離其宗。這里所說的文件結構是指將來要說的elf文件格式,在此不多說,留作伏筆。
正如之前所說,咱們只有用c語言的語法結構,這里沒有包含標準庫,也沒有直接的系統調用,以后咱們都得按照這種簡潔的方式編程啦。另外,有的同學已經注意到main.c所在的目錄啦,本來我還是想賣個關子的,但它所在的目錄出賣了我:在kernel目錄下。對,如您所想,它就是我們第一個內核文件,我們在project目錄下建立了個子目錄kernel,今后我們所有與內核相關的模塊都要放在此目錄下。
您也看到了,這個內核文件什么都沒做,通過while(1)這個死循環一直空兜cpu,目的是停在這里。想當初我就因為忘記加這樣的語句而導致不知道cpu執行到哪去了,當時排錯時可暈頭了,看到執行的指令都不是自己寫的,甚至都懷疑是虛擬機的問題,想想好慚愧啊,臉紅臉紅啊。當然查出來原因之后,自然又是滿地打滾,喜極而泣啦。這個簡單粗暴可依賴的死循環僅僅是為了演示elf文件解析以及加載內核的作用,今后我們要逐步完善它,看著它一點一點長大,就像是我們在養育孩子一樣,盡管我還沒有結婚^_^。
生成c語言程序的過程是這樣的。先將源程序編譯成目標文件(由c代碼變成匯編代碼后,再由匯編代碼生成二進制的目標文件),再將目標文件鏈接成二進制可執行文件。平時我們寫只有一個文件的小程序時,編譯器也是悄悄在背后這樣做的,除非加了參數讓編譯器分成兩個動作。由于咱們用的是c語言寫的程序,想到的是編譯器自然是大名鼎鼎的gcc,所以我們用gcc編譯該程序的參數是:
gcc -c -o kernel/main.o kernel/main.c,也許對其中的參數有的同學不太熟,沒關系,在執行gcc –help回車后,大家可以看到一些幫助信息,其中:
-c的作用是編譯、匯編到目標代碼,不進行鏈接,也就是直接生成目標文件。
-o的作用是將輸出的文件以指定文件名來存儲,有同名文件存在時直接覆蓋。
經過上面gcc的編譯后,我們得到了main.o文件,目前為止,它還是個“半成品”。為什么這么說呢,因為它只是個目標文件,也稱為待重定位文件,重定位指的是文件里面所用的符號還沒有安排地址,這些符號的地址需要將來與其它目標文件“組成”一個可執行文件時再重新定位(編排地址),這里的符號就是指該目標文件中所調用的函數或使用的變量,而這里的“組成”就是指鏈接。這些符號一般是位于其它文件中,所以在編譯時不能確定其地址,需要在所有目標文件都到齊了,將它們鏈接到一起時再重新定位(編排地址)。由于不知道可執行文件是由幾個目標文件組成,所以一律在鏈接階段對符號重新定位(編排地址)。所以說,哪怕是可執行文件只是由一個文件組成,其目標文件中的符號也是未編址的,編址工作,即重定位,一律統一在鏈接階段完成。
編譯成目標文件時也不我們可以用file命令檢查一下main.o的狀態。如file kernel/main.o,輸出見圖
為了讓大家更明顯地看出目標文件的可重定位屬性,我將relocatable用方框給大家圈出來了。
目標文件是可重定位文件,其中的符號都尚未“定位”,也就是符號(變量名,函數名)的地址尚未確定,這一點我們可以用linux的nm命令來查看。如圖
如圖所見,由于咱們的main.c過于簡單,里面只有一個符號,即main,所以nm只列出了它的符號信息。main函數的地址由于未被指定,所以其值為00000000。一會咱們鏈接后再對比下大家就更清楚了.
總結
以上是生活随笔為你收集整理的一步步编写操作系统 44 用c语言编写内核1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】聊聊分布式事务,再说说解决方案
- 下一篇: 一步步编写操作系统 28 cpu乱序执行