1. 程式人生 > >2018-2019-1 20189210 《LInux核心原理與分析》第四周作業

2018-2019-1 20189210 《LInux核心原理與分析》第四周作業

第三章
這一章接觸核心原始碼,對核心原始碼進行編譯和除錯跟蹤
一、預備知識:
核心:整個作業系統的最底層,它負責了整個硬體的驅動以及提供各種系統所需的核心功能。核心實質上是系統上面的一個檔案而已,這個檔案包含了驅動主機各項硬體的檢測程式與驅動模組。當系統讀完BIOS並載入MBR內的引導裝載程式後,就能夠載入核心到記憶體當中。然後核心開始檢測硬體,掛載根目錄並取得核心模組來驅動所有的硬體,之後呼叫/sbin/init就能依序啟動多有系統所需要的服務了。
Qemu :以GPL許可證分發原始碼的模擬處理器,能啟動那些為不同中央處理器編譯的Linux程式。

二、構造一個簡單的Linux核心
構造的MenuOS系統是由Linux核心映象和根檔案系統整合起來的。
Linux核心在PC上以檔案的形式存在(儲存成磁碟檔案形式),就是所謂的“映像檔案”。核心編譯(make)之後會生成兩個檔案,一個Image,一個zImage,其中Image為核心映像檔案,而zImage為核心的一種映像壓縮檔案,Image大約為4M,而zImage不到2M。為了能使用zImage這個壓縮版本,必須在它的開頭加上解壓縮的程式碼,將zImage 解壓縮之後才能執行,因此它的執行速度比Image要慢。但考慮到嵌入式系統的儲存空容量一般都比較小,核心要常駐記憶體,採用zImage可以佔用較少的儲存空間,因此犧牲一點效能上的代價也是值得的,所以一般嵌入式系統均採用壓縮的核心映像檔案,即zImage。
根檔案系統首先是一種檔案系統,該檔案系統不僅具有普通檔案系統的儲存資料檔案的功能,但是相對於普通的檔案系統,它的特殊之處在於,它是核心啟動時所掛載(mount)的第一個檔案系統,核心程式碼的映像檔案儲存在根檔案系統中,系統引導啟動程式會在根檔案系統掛載之後從中把一些初始化指令碼(如rcS,inittab)和服務載入到記憶體中去執行。先將/dev/ram0掛載,而後執行/linuxrc.等其執行完後。切換根目錄,再掛載具體的根檔案系統.根檔案系統執行完之後,也就是到了Start_kernel()函式的最後,執行init的程序,也就第一個使用者程序。對系統進行各種初始化的操作。

在實驗樓中的虛擬機器裡,直接進入LinuxKernel目錄使用如下命令就可以執行Linux核心原始碼和跟檔案系統

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rotes.img

其中bzImage是vmlinux經過gzip壓縮後的檔案,適用於大核心;vmLinux是編譯出來的最原始的核心ELF檔案;initrd是記憶體根檔案系統,只建立了一個rootfs.img,其中只有一個init的功能,用menu程式替代init, 核心啟動完成後進入menu程式。

三、跟蹤除錯Linux核心的啟動過程
Qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rotes.img -s -S

-S CPU初始化之前將其凍結起來

-s 是預設在tcp::1234埠上建立了一個gdb-server

再開啟一個視窗,啟動gdb,把核心載入進來,建立連線
file linux-3.18.6/vmlinux #把帶有符號表的核心映象載入進來
target remote:1234 #用tcp:1234埠來連結gdb server

然後在start_kernel函式和rest_init處設定端點,除錯執行

四、對於start_kernel( )和rest_init( )的分析
start_kernel( )相當於main.c中的main函式,是首先執行的,在此函式呼叫之前,核心程式碼主要是組合語言編寫的,用於完成硬體系統的初始化工作,為C程式碼的執行設定環境。這個函式主要進行各個模組初始化工作,trap_init()(中斷向量的初始化)、mm_init()(記憶體管理的初始化)sched_init()(排程模組的初始化)等。
init_task() :0號程序,初始化的起點
struct task_struct init_task = INIT_TASK(init_task);
可以看出 init_task(0號程序)是 task_struct 型別,是程序描述符,使用巨集INIT_TASK對其進行初始化,並且init_task是唯一沒有通過fork方式產生的程序。

rest_init( ):
在此函式中呼叫kernel_thread程序來建立kernel_init和kthreadd核心執行緒

kernel_init()核心執行緒為1號核心執行緒,負責執行核心的部分初始化工作及進行系統配置,最後呼叫do_execve載入init程式,最後演變成使用者態1號程序——init程序。
kthreadd()函式的任務是管理和排程其他核心執行緒 kernel_thread。for 迴圈中執行 kthread_create_list 全域性連結串列中維護的 kthread, 在create_kthread()函式中,會呼叫 kernel_thread 來生成一個新的程序並被加入到此連結串列中,因此所有的核心執行緒都是直接或者間接的以 kthreadd 為父程序。
小結:
本週學習了Linux核心原始碼,對於核心的原始碼目錄,編譯過程有了進一步的瞭解,但在學習中總會遇到新的問題,例如kernel_thread程序是由init零號程序fork出來的,以及idle程序與0號程序的關係,核心執行緒與使用者程序的區別,這些問題在CSDN論壇中大部分都以解決,但發現核心內部的知識非常高深,還需多多查閱資料