1. 程式人生 > >Linux核心初始化流程筆記

Linux核心初始化流程筆記

好文章,轉載一下!
作者:[email protected] 部落格:blog.focus-linux.net   linuxfocus.blog.chinaunix.net    本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文件的完整性,註明原作者及原連結,嚴禁用於任何商業用途。 ======================================================================================================
如前文http://http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=3129477的流程,bootloader將kerenel載入到記憶體中。
這裡載入的kernel映象,並不是真正的可執行檔案,而是一個壓縮的映象檔案。主要有兩種型別zImage和bzImage(即為Big zImage)。其中zImage小於512KB,而bzImage可以為一個大的壓縮映象檔案。zImage可以用於在沒有bootloader的情況下,直接啟動kernel。而
目前一般都是使用bzImage。以我目前的Fedora13為例,執行 file /boot/vmlinuz-2.6.33.3-85.fc13.i686.PAE
  1. /boot/vmlinuz-2.6.33.3-85.fc13.i686.PAE: Linux kernel x86 boot executable bzImage, version 2.6.33.3-85.fc13.i686.PAE (mock, RO-rootFS, root_dev 0x902, swap_dev 0x3, Normal VGA
為啥要壓縮呢?因為在這一時刻,CPU是工作在真實模式下,可訪問的記憶體空間只有1M,所以映象檔案要儘量的小,最好能夠小於1M。可是bzImage的大小往往會大於1M,怎麼辦?比如我的系統中:
  1. [[email protected] boot]# ls -lh vmlinuz-2.6.33.3-85.fc13.i686.PAE
  2. -rwxr-xr-x. 1 root root 3.4M May 7 2010 vmlinuz-2.6.33.3-85.fc13.i686.PAE
這個壓縮後的映象就已經高達3M了,遠超過真實模式下的1M定址空間。
解決方法很簡單,同樣是把kernel的映象也分為兩部分。第一部分為kernel執行在真實模式下boot sector(512位元組)和kernel setup(合計32K),而第二部分也是大部分程式碼都是執行在包含模式下。按照Linux的文件,推薦的記憶體佈局如下:

記憶體地址1M以下包括的kernel程式碼 1. boot sector:最前面的512位元組,該部分程式碼目前已經無用。原來是用於在沒有bootloader的情況下,啟動kernel。目前kernel的啟動必須依賴於bootloader。 2. setup:boot sector後面的程式碼,kernel啟動的真正入口,由bootloader直接跳轉到這裡。 3. stack/heap:真實模式下,核心需要的棧和堆,大小為32K。
關於kernel header的描述,可以檢視Document/x86/boot.txt。寫得很詳細
剩下的kernel程式碼被放置在0x100000即1M記憶體處,那麼毫無疑問,這部分程式碼是無法在真實模式下執行的,只能執行在保護模式下。


既然真實模式下核心程式碼的前512位元組已經作廢,所以真正的入口為setup,即arch/x86/boot下面的head.S。
  1. # End of setup header #####################################################
  2. .section ".entrytext", "ax"
  3. start_of_setup:
  4. #ifdef SAFE_RESET_DISK_CONTROLLER
  5. # Reset the disk controller.
  6. movw $0x0000, %ax # Reset disk controller
  7. movb $0x80, %dl # All disks
  8. int $0x13
  9. #endif

這裡為真正的入口點,從彙編的偽指令也很容易看出——即紅色那行。
head.S的程式碼的註釋很清晰,建立stack/heap,檢查簽名——驗證setup,清BSS段,然後call main函式——正常是不會返回的。當返回的時候,列印一些出錯資訊。
由head.S呼叫的main,需要注意的是,這時仍然是在真實模式下——因為沒有人啟用CPU的保護模式呢。head.S的主要任務是建立了一個C語言可執行的基本環境,然後由C程式碼去做進一步處理。
進入真實模式下的main,位於arch/x86/boot/main.c。程式碼註釋非常清楚,另一方面這太過於底層,基本上看一遍註釋即可。它的工作就是為進入保護模式作準備,然後呼叫go_to_protected_mode——這個仍然是C函式,先做切換到保護模式下的必要工作,然後呼叫protected_mode_jump——彙編去做真正的切換。
關於如何protected_mode_jump如何處理,如何enable CPU的保護模式。太多大神做過這些方面的說明了。另外,本文也只是我的一個筆記,對於這部分的細節,我暫時也沒有興趣。在protected_mode_jum的最後一條語句,將直接跳轉到保護模式下kernel的入口地址。這個地址是由header filed中的code32_start定義的,即為保護模式的kernel程式碼的入口地址。根據Linux的文件,這個地址是由bootloader使用,且可以bootloader來決定保護模式的kernel程式碼的載入地址。不管bootloader是否會更改保護模式的kernel程式碼的載入地址,反正在protected_mode_jump中,會使用bootloader確定的地址,然後跳轉到保護模式下的kernel入口——這個入口在vmlinux.lds.S定義。
對於32位的PC來說,入口為startup_32,位於head_32.S,再次進入彙編程式碼,不過也終於進入保護模式了。startup_32幹了啥,還是看註釋就清楚了,基本上還是一些準備工作,然後解壓核心,並將解壓後的核心仍然放在0x100000地址上,然後再次跳轉到0x100000處,執行解壓後的kernel程式碼。
這時kernel的入口仍然是startup_32,但是卻非之前的startup_32。前面的startup_32是位於arch/x86/boot/compressed/head_32.S,而現在這個startup_32為kernel解壓後的程式,其程式碼位於arch/x86/kernel/head_32.S。它的任務還是做一些準備工作,設定GDT,清BSS,初始化記憶體的page table,建立中斷表,等等。。。然後其呼叫i386_start_kernel->start_kernel。
start_kernel位於init/main.c,這個終於與平臺無關了,且進入了C程式碼。到此,Linux核心的初始化流程基本結束。進入start_kernel後,真正的kernel已經啟動,且進入了保護模式。後面的學習,就可以一步一步的看kernel是如何管理記憶體,程序排程,網路處理等等。
本文只能算是一個筆記,因為大部分都是查閱了別人的文章,幾乎沒有原創。並且內容如流水賬一般,只是簡單的將核心初始化流程走了一遍。底層的細節太多了,暫時也沒有精力去關注了。
當大致瞭解了kernel的載入,啟動的流程後,很多東西就沒有那麼神祕了。因為最起碼知道了kernel簡單的來龍去脈,為以後針對kernel具體部分的學習,做了鋪墊。
推薦大家閱讀一下我參考的文章: 1. http://duartes.org/gustavo/blog/post/kernel-boot-process