安大大 + 原創(chuàng)作品轉(zhuǎn)載請注明出處 + 《Linux操作系統(tǒng)分析》MOOC課程
實驗
- 使用自己的Linux系統(tǒng)環(huán)境搭建MenuOS的過程
# 下載內(nèi)核源代碼編譯內(nèi)核
cd ~/LinuxKernel/
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
xz -d linux-3.18.6.tar.xz
tar -xvf linux-3.18.6.tar
cd linux-3.18.6
make i386_defconfig
make # 一般要編譯很長時間蓝纲,少則20分鐘多則數(shù)小時
# 制作根文件系統(tǒng)
cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git # 如果被墻鹃共,可以使用附件menu.zip
cd menu
gcc -o init linktable.c menu.c test.c -m32 -static –lpthread #init是系統(tǒng)啟動后默認(rèn)啟動1號進(jìn)程遭贸,init是第一個用戶態(tài)進(jìn)程师坎,第一個用戶態(tài)進(jìn)程是1號進(jìn)程
cd ../rootfs
cp ../menu/init ./ #把init拷貝到rootfs目錄下邊
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img #使用cpio方式把當(dāng)前rootfs目錄下所有的文件打包成rootfs.img一個鏡像文件
#至此一個最簡單的根文件系統(tǒng)鏡像制作完畢
# 啟動MenuOS系統(tǒng)
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
- 重新配置編譯Linux使之?dāng)y帶調(diào)試信息
1.在原來配置的基礎(chǔ)上似袁,make menuconfig選中如下選項重新配置Linux,使之?dāng)y帶調(diào)試信息
2.1 kernel hacking—>
2.2 [*] compile the kernel with debug info #把debug信息打開
#使得跟蹤調(diào)試時可以邊跟蹤邊看跟蹤到某一點(diǎn)的某一行代碼時上下那一段的源代碼
3.make重新編譯(時間較長)
- 內(nèi)核啟動完成后進(jìn)入menu程序:
把menu編譯好的init文件放在rootfs里殴胧,然后把rootfs做成rootfs.img荚坞。根文件系統(tǒng)是rootfs.img,內(nèi)核啟動完后加載根文件系統(tǒng)澡谭,根文件系統(tǒng)里的init可執(zhí)行文件就被執(zhí)行起來了
使用gdb跟蹤調(diào)試內(nèi)核:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 關(guān)于-s和-S選項的說明:
-S freeze CPU at startup (use ’c’ to start execution) CPU初始化之前凍結(jié)起來
-s shorthand for -gdb tcp::1234 在這個端口上創(chuàng)建了一個gdb server愿题,若不想使用1234端口损俭,則可以使用-gdb tcp:xxxx來取代-s選項
另開一個shell窗口:
gdb
(gdb)file linux-3.18.6/vmlinux # 把一個帶有符號表的內(nèi)核鏡像加載進(jìn)來,在gdb界面中targe remote之前加載符號表
(gdb)target remote:1234 # 建立gdb和gdbserver之間的連接,按c 讓qemu上的Linux繼續(xù)運(yùn)行
(gdb)break start_kernel # 設(shè)置斷點(diǎn)潘酗,跟蹤內(nèi)核杆兵,斷點(diǎn)的設(shè)置可以在target remote之前,也可以在之后
全局變量init_task即手工創(chuàng)建的PCB在這里初始化,0號進(jìn)程即最終的idle進(jìn)程仔夺。
不管分析內(nèi)核的哪一部分都會涉及到start_kernel,因為要通過start_kernel進(jìn)行初始化琐脏。
trap_init();初始化一些中斷向量
mm_init();內(nèi)存管理模塊初始化
sched_init();調(diào)度模塊初始化
等等
start_kernel的最后一句rest_init();
當(dāng)系統(tǒng)沒有進(jìn)程需要執(zhí)行時就調(diào)度到idle進(jìn)程
Linux內(nèi)核啟動過程相關(guān)的參考資料
計算機(jī)的啟動過程概述
x86 CPU啟動的第一個動作CS:EIP=FFFF:0000H(換算為物理地址為000FFFF0H,因為16位CPU有20根地址線)缸兔,即BIOS程序的位置日裙。http://wenku.baidu.com/view/4e5c49eb172ded630b1cb699.html
BIOS例行程序檢測完硬件并完成相應(yīng)的初始化之后就會尋找可引導(dǎo)介質(zhì),找到后把引導(dǎo)程序加載到指定內(nèi)存區(qū)域后惰蜜,就把控制權(quán)交給了引導(dǎo)程序昂拂。這里一般是把硬盤的第一個扇區(qū)MBR和活動分區(qū)的引導(dǎo)程序加載到內(nèi)存(即加載BootLoader),加載完整后把控制權(quán)交給BootLoader抛猖。
引導(dǎo)程序BootLoader開始負(fù)責(zé)操作系統(tǒng)初始化格侯,然后起動操作系統(tǒng)。啟動操作系統(tǒng)時一般會指定kernel财著、initrd和root所在的分區(qū)和目錄联四,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash撑教,initrd (hd0,0)/myinitrd4M.img
內(nèi)核啟動過程包括start_kernel之前和之后朝墩,之前全部是做初始化的匯編指令,之后開始C代碼的操作系統(tǒng)初始化伟姐,最后執(zhí)行第一個用戶態(tài)進(jìn)程init收苏。
一般分兩階段啟動,先是利用initrd的內(nèi)存文件系統(tǒng)玫镐,然后切換到硬盤文件系統(tǒng)繼續(xù)啟動倒戏。initrd文件的功能主要有兩個:1、提供開機(jī)必需的但kernel文件(即vmlinuz)沒有提供的驅(qū)動模塊(modules) 2恐似、負(fù)責(zé)加載硬盤上的根文件系統(tǒng)并執(zhí)行其中的/sbin/init程序進(jìn)而將開機(jī)過程持續(xù)下去
Linux內(nèi)核中的init_task進(jìn)程和idle進(jìn)程
道生一(start_kernel....cpu_idle)杜跷,一生二(kernel_init和kthreadd),二生三(即前面0矫夷、1和2三個進(jìn)程)葛闷,三生萬物(1號進(jìn)程是所有用戶態(tài)進(jìn)程的祖先,2號進(jìn)程是所有內(nèi)核線程的祖先)双藕,新內(nèi)核的核心代碼已經(jīng)優(yōu)化的相當(dāng)干凈淑趾,都符合中國傳統(tǒng)文化精神了。