Linux 內核分析——【實驗三:初探Linux內核源代碼】
前面兩節介紹了計算機的基本工作方式,包括棧在程序執行過程中的作用,以及進程之前的調用。總的來說,有幾點必須要牢記的:
1)目前計算機採用的都是馮諾依曼體系結構,它的特點是“程序存儲,順序執行”;
2)計算機能夠處理多任務,主要是採用了中斷的機制,中斷正在執行的程序,保存現場,實現進程上下文切換;
3)棧在函數調用和中斷過程起着至關重要的作用,它實現了多個進程“同時”執行的可能。
下面,我們將對Linux內核源代碼進行簡要分析,瞭解它的主要組成部分,每部分大致功能是做什麼,程序的入口在哪?
首先,搭載實驗環境,步驟如下:
(1)下載內核源代碼,編譯內核
~$ cd Linux/Lab3
~$ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz #下載Linux-3.18.6版本內核代碼
~$ xz -d linux-3.18.6.tar.xz #解壓縮
~$ tar -xvf linux-3.18.6.tar #解包
~$ cd linux-3.18.6
~$ make i386_defconfig #配置編譯環境
~$ make # 進行編譯
(2)製作根文件系統,內核系統運行時需要掛載它
~$ cd Linux/Lab3
~$ mkdir rootfs #創建一個根文件系統
~$ git clone https://github.com/mengning/menu.git #下載menu,用於製作根文件系統
~$ cd menu
~$ gcc -o init linktable.c menu.c test.c -m32 -static –lpthread #編譯鏈接,輸出爲可執行文件init
~$ cd ../rootfs
~$ cp ../menu/init ./ #複製init到rootfs目錄
~$ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img #將rootfs下init文件打包成rootfs.img文件
~$ cd .. && ls #查看文件是否製作成功,如下**圖3-1**
3)啓動MenuOS系統
~$ cd Linux/Lab3
~$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img #-initrd rootfs.img就是掛載文件系統 如下圖3-1
4)爲了方便後面更加清楚地瞭解內核代碼運行情況,我們需要在編譯的時候加上調試信息,重新設置編譯環境
~$ make menuconfig #使用圖形化界面設置編譯環境,與1)中的"make i386_defconfig"對應,在linux-3.18.6目錄下
#如下圖3-2,圖3-3,圖3-4
#注意:這裏可能會報錯,需要安裝一個ncurses庫(ubuntu系統默認沒有安裝), ~$ sudo apt-get install libncurses5-dev 可以百度一下
~$ make #重新編譯
5)編譯好後我們就可以用gdb進行調試,如下圖3-5
~$ qemu arch/x86/boot/bzImage -initrd ../roorfs.img -s -S #關於 -s ,-S 選項說明如下:
#-S 在cpu啓動時暫停
#-s 爲gdb提供一個調試端口tcp:1234 ,如是不想使用1234端口,可通過-gdb tcp::xxxx 來代替-s
6)新建一個shell終端(可以在當前shell窗口右鍵新建一個終端)
~$ gdb -q #進入gdb調試,-q阻止打印gdb的相關信息
(gdb) file vmlinux #加載符號表
(gdb) target remote:1234 #建立gdb和gdbserver之間的連接
(gdb) break start_kernel #設置斷點
(gdb) c #continue 讓qemu上的linux運行
(gdb) list #程序會運行到start_kernel的地方然後暫停,list可以查看代碼,如下圖3-6
此外,我們還可以使用更多的gdb調試命令來跟蹤linux內核的運行過程,如下:
相關gdb調試命令
(1) clear #清除所有斷點
(2) delete [linenum] #清除代碼中行數爲linenum的斷點
(3) disable [linenum] #取消斷點,可通過enable恢復
(4) step [count] #單步調試count步,會進入調用的函數中
(5) next [count] #單步調試count步,不會進入調用的函數
(6) finish #當前函數執行到返回
(7) information frame #顯示棧信息
(8) search < regexp> #搜索源代碼
(9) print < var>/< register>/< memory> #查看數據
(10) information registers #查看所有寄存器信息
相關截圖
圖3-1 根文件所有目錄
圖3-2 make menuconfig配置過程,選擇”Kernel hacking —> ” 回車進入
圖3-3 選擇”Comile-time checks and comp[iler options —>” 回車進入
圖3-4 找到”Compile the kernel with debug info”,輸入星號”*”, 選擇”save”保存,再依次退出
圖3-5 使用gdb調試
圖3-6 具體調試過程
接下來,我們來看一下,linux內核代碼的目錄結構
其中,init目錄是關於內核初始化的,該目錄下的main.c文件中,有一個start_kernel的函數,它可以看作是系統啓動的入口。
計算機在開機時,會先讓cpu的CS:EIP指向BIOS所在位置,即先執行BIOS中的指令,BIOS程序會進行硬件檢測和相關初始化,然後會把引導程序(即BootLoader,一般在硬盤的第一個扇區MBR)加載到內存中,接着將控制權交給引導程序,實現開機過程。而引導程序需要負責操作系統的初始化,然後再啓動系統。這整個過程就包括了在start_kernel函數之前和之後兩部分,之前部分全部是初始化的彙編指令,之後部分是初始化的C代碼,因爲C語言也需要構造一個合適的運行環境,以便於啓動操作系統。此外,啓動時還需要指定kernel,initrd(初始RAM磁盤,在實際根系統文件可用之前臨時掛載到系統中的一個臨時根文件系統),和root所在的分區和目錄。當所有初始化工作完成後,會啓動一個0號進程,它處於死循環狀態,等待用戶任務的到來。當有任務時,則通過中斷,跳轉出去執行其他任務,執行完後又返回來。
=========== 王傑 原創作品轉載請註明出處==============
《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000 ”