網(wǎng)易云課堂《Linux內(nèi)核分析》作業(yè)
實(shí)驗(yàn)?zāi)康模?br>
使用gdb跟蹤分析一個(gè)系統(tǒng)調(diào)用中斷處理過程,分析系統(tǒng)調(diào)用從system_call開始到iret結(jié)束之間的整個(gè)過程抹缕。
實(shí)驗(yàn)過程:
登陸實(shí)驗(yàn)樓虛擬機(jī)http://www.shiyanlou.com/courses/195
打開shell終端肺稀,執(zhí)行以下命令:
cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git
獲取到MenuOS源碼后修改test.c文件滑蚯,加入getuid系統(tǒng)調(diào)用函數(shù)源碼蕉饼,Getuid為調(diào)用C API版本尊流,GetuidAsm為內(nèi)嵌匯編語言版本
增加命令菜單
在menu目錄下執(zhí)行
make rootfs
改造后的MenuOS系統(tǒng)會(huì)自動(dòng)編譯并啟動(dòng)運(yùn)行
執(zhí)行g(shù)etuid和getuid-asm命令可以看到執(zhí)行結(jié)果
結(jié)束qmeu帅戒,以調(diào)試方式啟動(dòng),在LinuxKernel目錄下執(zhí)行
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
打開另一個(gè)shell終端崖技,進(jìn)入gdb調(diào)試工具
gdb
讀入符號(hào)表
file linux-3.18.6/vmlinux
連接內(nèi)核調(diào)試
target remote:1234
設(shè)置斷點(diǎn)
b sys_getuid16
繼續(xù)執(zhí)行
c
列出代碼
list
繼續(xù)單步執(zhí)行后續(xù)操作
s
實(shí)驗(yàn)分析:
通過上述實(shí)驗(yàn)過程可以分析出在Linux系統(tǒng)下發(fā)生一次系統(tǒng)調(diào)用后system_call中斷處理的整個(gè)過程逻住。
當(dāng)在應(yīng)用程序中調(diào)用獲取當(dāng)前用戶uid函數(shù)getuid(),將對(duì)應(yīng)的系統(tǒng)調(diào)用號(hào)0x18送入eax寄存器迎献,由于沒有其他參數(shù)瞎访,ebx寄存器賦值為0,執(zhí)行init 0x80匯編語言指令吁恍,從用戶態(tài)切換到內(nèi)核態(tài)扒秸,觸發(fā)系統(tǒng)調(diào)用中斷播演,在system_call的最后是iret匯編語言指令,從系統(tǒng)調(diào)用退出伴奥,從內(nèi)核態(tài)切換回用戶態(tài)写烤。
system_call()函數(shù)
首先把系統(tǒng)調(diào)用號(hào)和這個(gè)異常處理程序可以用到的所有CPU寄存器保存到相應(yīng)的棧中,不包括由控制單元已自動(dòng)保存的eflags拾徙、cs洲炊、eip、ss和esp寄存器尼啡。
pushl %eax
SAVE_ALL
movl $0xffffe000, %ebx /* or 0xfffff000 for 4-KB stacks */
andl %esp, %ebx
接下來檢查thread_info結(jié)構(gòu)flag字段的TIF_SYSCALL_TRACE和TIF_SYSCALL_AUDIT標(biāo)志之一是否被置為1暂衡,即檢查是否有某一調(diào)試程序正在跟蹤執(zhí)行程序?qū)ο到y(tǒng)調(diào)用的調(diào)用。
如果系統(tǒng)調(diào)用號(hào)無效則把-ENOSYS值存放在棧中曾保存eax寄存器的單元中崖瞭,當(dāng)進(jìn)程恢復(fù)在用戶態(tài)的執(zhí)行時(shí)會(huì)在eax中得到一個(gè)負(fù)的返回碼狂巢。
cmpl $NR_syscalls, %eax
jb nobadsys
movl $(-ENOSYS), 24(%esp)
jmp resume_userspaces
最后調(diào)用與eax中所包含的系統(tǒng)調(diào)用號(hào)對(duì)應(yīng)的特定服務(wù)例程。
call *sys_call_table(0, %eax, 4)
從系統(tǒng)調(diào)用退出
當(dāng)系統(tǒng)調(diào)用服務(wù)例程結(jié)束時(shí)书聚,system_call()函數(shù)從eax獲得返回值唧领,并保存在曾經(jīng)保存用戶態(tài)eax寄存器值的棧單元位置上,用戶態(tài)進(jìn)程將在eax中找到系統(tǒng)調(diào)用的返回碼寺惫。
movl %eax, 24(%esp)
system_call()函數(shù)關(guān)閉本地中斷并檢查當(dāng)前進(jìn)程的thread_info結(jié)構(gòu)中的標(biāo)志疹吃,如果所有的標(biāo)志都沒有被設(shè)置函數(shù)就會(huì)跳轉(zhuǎn)到restore_all標(biāo)記處,恢復(fù)保存在內(nèi)核棧中的寄存器的值西雀,并執(zhí)行iret匯編語言指令以重新開始執(zhí)行用戶態(tài)進(jìn)程。
cli
movl 8(%ebp), %ecx
testw $0xffff, %cx
je restore_all
實(shí)驗(yàn)總結(jié):
Linux內(nèi)核在啟動(dòng)初始化期間會(huì)調(diào)用trap_init()函數(shù)設(shè)置向量128(十六進(jìn)制0x80)對(duì)應(yīng)的內(nèi)核入口點(diǎn)歉摧。當(dāng)初始化完畢后艇肴,程序需要執(zhí)行系統(tǒng)調(diào)用時(shí),只需執(zhí)行init 0x80語句叁温,發(fā)生一個(gè)向量128的中斷即可再悼。
系統(tǒng)調(diào)用本質(zhì)上是發(fā)生了一次軟件中斷。
部分資料參考《深入理解Linux內(nèi)核》
aapu原創(chuàng)作品轉(zhuǎn)載請(qǐng)注明出處《Linux內(nèi)核分析》MOOC課