2018-2019-1 20189203《Linux核心原理與分析》第四周作業
阿新 • • 發佈:2018-11-01
第一部分 課本學習
- 核心版本號:Linux核心自2013年12月起,就以A.B.C.D的方式命名。A和B變得無關緊要,C是核心的真實版本。每一個版本的變化都會帶來新的特性,如內部API的變化等,改動的程式碼數量常常上萬行。D是安全補丁和bug修復。
- 幾個關鍵的目錄:
Arch:與體系結構相關的子目錄列表。
Block:存放Linux儲存體系中關於塊裝置管理的程式碼。
Crypto:存放常見的加密演算法的C語言程式碼。
Documentation:存放一些文件。
Drivers:驅動目錄,裡面分門別類地存放了Linux核心支援的所有硬體裝置的驅動原始碼。
Firmware:韌體。
Fs:檔案系統,裡面列出了Linux支援的各種檔案系統的實現。
Include:標頭檔案目錄,存放公共的標頭檔案。
Init:init是初始化的意思,存放Linux核心啟動時的初始化程式碼。
Ipc:裡面是Linux支援的IPC的程式碼實現。
Kernel:存放核心本身需要的一些核心程式碼檔案。其中有很多關鍵程式碼,包括pid--程序號等。
Lib:公用的庫檔案,裡面是一些公用的庫函式。
Mm:存放LInux的記憶體管理程式碼。
Net:該目錄下是網路相關的程式碼,譬如TCP/IP協議棧等。 - 編譯配置Linux核心的關鍵步驟:
1、編譯安裝步驟
安裝開發組包>下載原始碼檔案>.config: 準備配置檔案>make menuconfig: 配置核心選項>make[-j#]>make modules_install: 安裝模組>make install:安裝核心相關檔案>安裝bzImage 為 /boot/vmlinuz-VERSION-RELEASE>生成initramfs>編輯grub的配置檔案。
2、編譯配置選項
(1)配置核心選項
(2)支援“更新”模式進行配置:make help
Make config:基於命令列以遍歷的方式去配置核心中可配置的每個選項。
Make menuconfig:基於命令列以遍歷的方式去配置核心中可配置的每個選項。
Make gconfig:基於GTK(GNOME)環境視窗介面。
Make xconfig: 基於QT(KDE)環境的視窗介面。
(3)支援“全新配置”模式進行配置
Make defconfig:基於核心為目標平臺提供的“預設”配置進行配置。
Make allyesconfig:所有選項均回答為“yes”。
Make allnoconfig:所有選項均回答為“no”。
3.編譯
全編譯:make[-j#]
第二部分 實驗操作
- 在實驗樓環境下輸入:
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
完成啟動核心的操作,如下圖:
- 幾個指令的含義:
qemu :相當於開啟一個虛擬機器
kernel:啟動一個核心,位置由其後的檔名指定。
bzImage是vmLinux經過gzip壓縮後的檔案,是壓縮的核心映像。vmLinux是編譯出來的最原始的核心ELF檔案。
initrd是“initial ramdisk”的簡寫,普通linux使用者一般感受不到這個記憶體根檔案系統的存在,核心啟動先訪問initrd檔案系統,然後在切換到磁碟檔案系統。 - 使用gdb跟蹤除錯核心
(gdb)file linux-3.18.6/vmlinux # 在gdb介面中targe remote之前載入符號表
(gdb)target remote:1234 # 建立gdb和gdbserver之間的連線,按c 讓qemu上的Linux繼續執行
(gdb)break start_kernel # 斷點的設定可以在target remote之前,也可以在之後
操作如下:
載入符號表:
連結gdbserver:
設定一個斷點
檢視start_kernel函式的上下文
第三部分 分析從start_kernel到init程序啟動的過程
1、start_kernel()
程式碼如下:
main.c中沒有main函式,start_kernel()相當於C語言中的main函式。Start_kernel是一切的起點,在此函式被呼叫之前,核心程式碼主要是用匯編語言寫的,用於完成硬體系統的初始化工作,為C程式碼的執行設定環境。
2、init_task()
程式碼如下:
init_task(0號程序)是task_struct型別,是程序描述符,使用巨集INIT_TASK對其進行初始化。接下來就是對各模組的初始化。
3.rest_init()
程式碼如下:
rest_init()這個函式呼叫系統函式 kernel_thread() 建立 1 號程序,即 init 程序,是使用者態所有程序的祖先。然後,新建 kthreadd 程序,是核心態所有程序的祖先。最後,通過 cpu_startup_entry 函式啟動 0 號程序。
Init_task()(PID為0)在建立了init程序後,呼叫cpu_idle()演變成idle程序,執行一次排程後,init程序執行。1號核心執行緒負責執行核心的部分初始化工作及進行系統配置,最後呼叫do_execve載入init程式,演變成init程序(使用者態1號程序),init程序是核心啟動的第一個使用者態程序。Kthreadd(PID為2)程序由0號程序建立,始終執行在核心空間,負責所有核心執行緒的排程和管理。