2018-2019-1 20189206 《Linux內核原理與分析》第六周作業
linux內核分析學習筆記 ——第五章 系統調用的三層機制
學習重點——深入理解系統調用的過程
給MenuOS添加命令
添加命令的方式較為簡單,在LinuxKernel/menu/test.c
目錄下,打開test.c
,main函數中的MenuConfig就是對應的系統調用的功能增加,之後在上面給出具體實現即可。之後就可以在MenuOS中使用該系統調用函數。
使用gdb跟蹤內核函數sys_getuid
上面在MenuOS中添加了功能,我這裏改成了getuid獲得當前用戶的id號,調試步驟和實驗三基本一樣
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
凍結內核運行,然後利用gdb調試工具,加載內核,連接端口1234
file linux-3.18.5/vmlinux
target remote:1234
之後在系統調用sys_getuid
處設置斷點,當啟動MenuOS調用功能Getuid後 一旦調用系統函數,程序暫停。可以看到程序停在了sys_getuid
處,但該系統調用是利用宏函數實現的,經過預編譯處理後是sys_getuid
的形式。
在system_call
位置設置斷點並不能停下,還是會停在sys_getuid這裏,因為system_call並不是一個正常的函數,只是一段匯編代碼的起點,內部沒有嚴格的函數調用堆棧機制,所以gdb不能完成跟蹤任務
系統調用的處理過程
在用戶態中有一個c語言提供的APIxyz()
,在用戶看來這就是系統調用,而xyz()
中調用了SYSCALL
來觸發系統調用。
即中斷向量0x80對應system_call
中斷服務程序入口。
start_kernel函數執行內核啟動的初始化工作,其中會調用trap_init函數,在上圖所示目錄中可以看到以下代碼。
當接受到int 0x80 中斷請求後,通過set_sysytem_trap_gate
函數將中斷向量和入口函數綁定,將直接進入中斷,也就是跳轉到system_call
的位置。
這裏將trap_init
中綁定中斷向量和入口函數的代碼中的宏定義實現,可以看到系統調用對應的中斷向量就是0x80
system_call函數的理解
system_call
是一段中斷服務程序入口的匯編代碼,就是系統調用的處理過程,是系統調用用戶態和系統調用內核態想轉換的過程。
中斷過程都會有現場保護和恢復現場代碼一開始的SAVE_ALL和restore_all就是實現了中斷上下文過程。
sys_call_table
是一個系統調用表,EAX寄存器保存了傳遞的系統調用號,來調用相應的系統調用,調用結束,EAX寄存器還要保存系統調用的返回值。
syscall_exit中判斷當前的任務是否需要進程調度,如果需要進程調度進入syscall_exit_work
,執行進程調度,結束後恢復現場返回用戶態。
系統調用函數
上圖所示是系統調用system_call的流程圖
- SAVE ALL 是中斷發生後保存現場的宏
cmpl $(nr_syscalls),%eax
判斷檢查系統調用號的合法性如果系統調用號大於nr_syscalls 那麽出現異常,跳入異常處理syscall_badsys
-call *sys_call_table(,%eax,4)
通過系統調用號在系統調用表中找到相應的系統調用內核處理函數,就是將函數API和系統調用聯系起來。movl %eax,PT_EAX(%esp)
作用是將系統調用返回值入棧syscall exit
檢查是否有任務要處理,如果發生進程調度需要進入syscall_exit_work
- 進程間通信會有信號發生,所以進入進程調度後會有
work_pending
、work_notifysig
信號處理、和work_resched
等處理work_resched
中的schedule存著進程調度的代碼,也就是進程調度的時機點
- 進程間通信會有信號發生,所以進入進程調度後會有
restore_all
和iret
用於恢復現場和返回用戶態。
Linux內核編譯實踐中遇到的問題
- 在為函數添加系統調用號時的路徑問題
sudo gedit /usr/src/linux-3.14.40/arch/x86/syscall/syscall_32.tbl。
根據文檔我在這個路徑下並沒有找到syscall文件夾,也就無法修改 syscall_32.tbl
最後參考了一篇博客,我想問題應該是內核版本更新,目錄有了一定的修改,最後找到了路徑
- 使用
make bzImage
時出現錯誤
根據錯誤提示,我考慮問題出在了內核調用時添加的系統調用格式出現了問題對比之後,我對代碼進行更改,結果編譯成功
內核編譯時間非常久,編譯好了之後無法啟動,錯誤問題如下
後來發現是虛擬機的內存不足的原因,重新設置了虛擬機的內存之後成功進入虛擬機,利用命令可以看到該內核版本已經是剛剛編譯的4.18.18
寫一個程序調用我們剛剛創建的系統調用,返回值得到的是我們當時設定的返回值
參考這篇博客給我提供了解決問題的很多靈感:
Linux4.18.9添加系統調用傳遞參數示例
2018-2019-1 20189206 《Linux內核原理與分析》第六周作業