Linux系統啟動初始化的主要流程是:
1.上電BIOS自檢
2.啟動Boot Loader(GRUB)
3.加載內核
4.啟動第一個進程
5.配置環境
一.BIOS 加載啟動引導程序
BIOS (英文: B asic I nput/ O utput S ystem),即 基本輸入輸出系統 ,亦稱為ROM BIOS、System BIOS、PC BIOS,是在通電引導階段運行硬件初始化,以及為 操作系統 提供運行時服務的 固件 。BIOS最早隨着 CP/M 操作系統的推出在1975年出現。BIOS預安裝在 個人電腦 的 主板 上,是 個人電腦 啟動時加載的第一個軟件。
現在,BIOS的作用是初始化和測試 硬件 組件,以及從大容量存儲設備(如硬盤)加載 引導程序 ,並由 引導程序 加載操作系統。BIOS還為 DOS 操作系統提供鍵盤、顯示及其他 I/O 設備的 硬件抽象層 。
許多BIOS程序都只能在特定電腦型號或特定主板型號上運行。早年,BIOS存儲於 ROM 芯片上;現在的BIOS多存儲於 閃存芯片上,這方便了BIOS的更新。
計算機上電之后,CPU就可以執行程序了,但是此時內存中並沒有程序讓CPU執行,因為內存是RAM掉電丟失。而且操作系統也沒有裝上,這個時候就要裝系統了。ROM中就固化了一些計算機剛上電要執行的初始化程序,也就是BIOS(Basic Input and Output System,基本輸入輸出系統) 。簡單地理解 BIOS,它就是固化在主板上一個 ROM(只讀存儲器)芯片上的程序, 主要保存計算機的基本輸入/輸出信息、系統設置信息、開機自檢程和系統自啟動程序,用來為 計算機提供最底層和最直接的硬件設置與控制。
BIOS 的初始化主要完成以下 3 項工作:
- 第一次檢查計算機硬件和外圍設備(第二次自檢由內核完后,后續會講),例如 CPU、內存、風扇燈。當 BIOS 一啟動,就會做一個 自我檢測的工作 ,整個自檢過程也被稱為 POST(Power On Self Test)自檢。
- 如果自檢沒有問題, BIOS 開始對硬件進行初始化 ,並規定當前可啟動設備的先后順序,選擇由那個設備來開機。
- 選擇好開啟設備后, 就會從該設備的 MBR(主引導扇區)中讀取 Boot Loader(啟動引導程序)並執行。啟動引導程序用於引導操作系統啟動,Linux 系統中默認使用的啟動引導程序是 GRUB。
BIOS從MBR中讀取啟動引導程序,加載至RAM中,就可以執行啟動引導程序了。
二.MBR 主引導扇區
MBR是用來存儲啟動引導程序的!!!
MBR 也就是主引導扇區 ,位於硬盤的 0 磁道、0 柱面、1 扇區中,主要記錄了啟動引導程序和磁盤的分區表 ,如圖是MBR的結構:
MBR大小是一個扇區512Byte, 其中 446 Byte 安裝了啟動引導程序,其后 64 Byte 描述分區表,最后的 2 Byte 是結束標記 。
BIOS從MBR中讀取啟動引導程序,將啟動引導程序加載至RAM中,然后BIOS將控制權交給啟動引導程序。
所以,雖然啟動引導程序是在MBR中的,但是實際上是由BIOS從MBR中將啟動引導程序加載至RAM中運行的!
注意:這里的446Byte中存放的只是啟動引導程序的一個鏡像文件,后面還要通過這個鏡像文件來加載出完整的啟動引導程序!
三.GRUB引導內核
Linux下是通過一個工具Grub2( Grand Unified Bootloader Version 2 ),這個工具就是專門引導系統啟動的。
GNU GRUB (簡稱“GRUB”)是一個來自 GNU項目 的 啟動引導程序 。GRUB是 多啟動規范 的實現,它允許用戶可以在計算機內同時擁有多個操作系統,並在計算機啟動時選擇希望運行的操作系統。GRUB可用於選擇操作系統分區上的不同 內核 ,也可用於向這些內核傳遞啟動參數。
3.1運行 boot.img
BootLoader,是啟動引導程序, 啟動引導程序的主要任務就是加載操作系統內核 , 每種操作系統的文件格式不同,因此,每種操作系統的啟動引導程序也不一樣。不同的操作系統只有使用自己的啟動引導程序才能加載自己的內核。這里使用GRUB2作為啟動引導程序。
上面說BIOS將啟動引導程序加載到RAM中執行,但是 實際上啟動引導程序的大小比512Byte要大,所以MBR中的只是一個鏡像文件:boot.img
。我們還得通過MBR中的 boot.img
找到完整的啟動引導程序,從而正式啟動內核。
所以, boot.img
的任務並不是啟動內核,而是加載其他鏡像文件來完成內核的啟動,也可以理解為只有446Byte大小的 boot.img
不足以啟動內核,只能召喚出更加強大厲害的其他鏡像文件來啟動內核。
3.2加載 core.img
這個強大的后援就是: core.img
, core.img 由 lzma_decompress.img、diskboot.img、kernel.img 和一系列的模塊組成,功能比較豐富,能做很多事情。
boot.img 先加載的是 core.img 的第一個扇區。如果從硬盤啟動的話,這個扇區里面是 diskboot.img,對應的代碼是 diskboot.S。boot.img 將控制權交給 diskboot.img 后,diskboot.img 的任務就是將 core.img 的其他部分加載進來,先是解壓縮程序 lzma_decompress.img,再往下是 kernel.img,最后是各個模塊 module 對應的映像。這里需要注意,它不是 Linux 的內核,而是 grub 的內核。lzma_decompress.img 對應的代碼是 startup_raw.S,本來 kernel.img 是壓縮過的,現在執行的時候,需要解壓縮。
在這之前,我們所有遇到過的程序都非常非常小,完全可以在實模式下運行,但是隨着我們加載的東西越來越大,實模式這 1M 的地址空間實在放不下了, 所以在真正的解壓縮之前,lzma_decompress.img 做了一個重要的決定,就是調用 real_to_prot,切換到保護模式 ,這樣就能在更大的尋址空間里面,加載更多的東西。
3.3切換到保護模式
切換到保護模式需要做以下幾點:
- 啟用分段,在內存中建立段描述符表(和中斷向量表差不多),原來的段寄存器指向現在的段描述符
- 啟動分頁,現在內存大了,需要分頁進行管理
- 打開Gate A20,使用第21根地址控制線
3.4kernel.img 引導內核
這里kernel.img不是Linux的內核,而是GRUB的內核。
kernel.img 對應的代碼是 startup.S 以及一堆 c 文件,在 startup.S 中會調用 grub_main,這是 grub kernel 的主函數,主函數中 grub_load_config() 開始解析,我們上面寫的那個 grub.conf 文件里的配置信息。
當啟動了操作系統后,就要開始調用 grub_menu_execute_entry() ,開始解析並執行你選擇的那一項。
例如里面的 linux16 命令,表示裝載指定的內核文件,並傳遞內核啟動參數。於是 grub_cmd_linux() 函數會被調用,它會首先讀取 Linux 內核鏡像頭部的一些數據結構,放到內存中的數據結構來,進行檢查。如果檢查通過,則會讀取整個 Linux 內核鏡像到內存。
四.內核初始化
上面已經引導了操作系統,也就是引導了內核,接下來就是內核的初始化了。
內核啟動的入口函數從 start_kernel()
開始, 在 init/main.c
文件中,start_kernel 相當於內核的 main 函數。打開這個函數,你會發現,里面是各種各樣初始化函數 XXXX_init .
內核初始化做了以下這些事:
- 創建 0號進程, 這是唯一一個沒有通過 fork 或者 kernel_thread 產生的進程 ,托福閱讀評分標准是進程列表的第一個。
- 初始化中斷, 對應的函數是 trap_init() ,里面設置了很多中斷門(Interrupt Gate),用於處理各種中斷。其中有一個 set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_32),這是系統調用的中斷門 。系統調用也是通過發送中斷的方式進行的。
- 初始化內存管理模塊, mm_init()
- 初始化調度模塊, sched_init()
- 初始化基於內存的文件系統rootfs, vfs_caches_init()
- 初始化其他, rest_init()
- 創建用 戶態第一個進程 :1號進程,用 kernel_thread(kernel_init, NULL, CLONE_FS) 創建1號進程
- 創建內核態第一個進程:2號進程, kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES)
這樣看來,內核初始化靠的是 start_kernel()
函數,初始化主要做三件事:
1.創建樣板進程(也就是0號進程),以及各個模塊的初始化
2.創建用戶態的進程
3.創建內核態的進程
用戶態訪問核心資源時,通過中斷來請求,就是系統調用的統一中斷,流程如下:
用戶態和內核態進程執行的框圖如下:
五.系統調用
從上面得知,一般程序運行在用戶態,如果要想使用核心資源,就要進入內核態,就要通過系統調用。
此外,linux的一些驅動程序都是寫在內核中的,當上層應用想要調用驅動的接口時,也要通過系統調用來實現。
但是,直接操作系統調用比較繁瑣,所以一般使用glibc庫對系統調用進行封裝。
64位的系統調用如圖所示: