Linux核心分析——扒開系統呼叫的三層皮(下)
張瑜
《Linux核心分析》MOOC課程
http://mooc.study.163.com/course/USTC-1000029000
一、實驗內容
1. 通過核心的方式使用系統呼叫
上週是從使用者態來看系統呼叫,這周是從核心方面來看這個問題
用到的命令:
rm menu -rf //強制刪除當前menu
git clone http://git.shiyanlou.com/mengning/menu.git //重新克隆新版本的menu
cd menu
ls
make rootfs //rootfs是事先寫好的一個指令碼,自動編譯自動生成根檔案系統,同時自動啟動MenuOS
2. 將上週選擇的系統呼叫新增到MenuOS中
在menu資料夾中的 test.c檔案中,新增上週寫的Gitpid和Gitpidasm程式碼
int Getpid(int argc , char * argv[])
{
int pid;
pid=getpid();
printf("pid=%d\n",pid);
return 0;
}
int Getpidasm(int argc , char *argv[])
{
int pid;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x14,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
:"=m"(pid)
);
printf ("pid = %d\n",pid);
return 0;
}
//在main函式中新增
MenuConfig(“getpid","Show pid",Getpid);
MenuConfig("getpid-asm","Show pid(asm)",Getpidasm);
重新make
3.使用gdb跟蹤分析這該系統呼叫核心函式
用到的命令:
qemu -kernel linux.3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 除錯。
file linux-3.18.6/vmlinux 載入除錯核心符號表。
b 設定斷點 n 單步執行
二、系統呼叫過程分析
1. 系統呼叫在核心程式碼中的工作機制和初始化
int 0x80——>system call:通過中斷向量匹配
system call——>sys_xyz():通過系統呼叫號匹配
一旦執行int 0x80後立刻跳轉到system_call執行
2. 系統呼叫system_call的處理過程
syscall_call 函式到系統呼叫服務例程通過系統呼叫號聯絡起來:
在上面執行軟中斷 0x80 時,系統呼叫號會被放入eax暫存器(引數的傳遞);
函式讀取eax暫存器獲取引數(當前系統呼叫的呼叫號),將其乘以4生成偏移地址。
其中 sys_call_table 基址在檔案arch/x86/kernel/syscall_table_32.S 中定義,同時表中每一項例程的地址佔用4個位元組,所以上面乘以4。
由於系統呼叫例程在定義時時用 asmlinkage 標記了的,所以編譯器僅從堆疊中獲取該函式的引數。在進入system_call函式前,使用者應用會把引數存放到暫存器中,system_call 函式執行時會首先把這些暫存器壓入堆疊。這樣對系統呼叫服務例程可以直接從堆疊照片能夠獲取引數。
3.從system_call開始到iret結束之間的過程
從整體過程來看,系統通過 int 0x80 從使用者態進入核心態。在這個過程中系統先儲存了中斷環境,然後執行系統呼叫函式。system_call() 函式通過系統呼叫號查詢系統呼叫表 sys_cal_table 來查詢到具體的系統呼叫服務程序。在執行完系統呼叫後在執行 iret 之前,核心做了一系列檢查,用於檢查是否有新的中斷產生。如果沒有新的中斷,則通過已儲存的系統中斷環境返回使用者態。這樣就完成了一個系統呼叫過程。系統呼叫通過 INT 0x80 進入核心,跳轉到system_call() 函式,然後執行相應服務程序。因為代表了使用者程序,所以這個過程並不屬於中斷上下文,而是屬於程序上下文。
4.系統呼叫處理過程的彙編程式碼分析
無論是中斷返回(ret_from_intr) ,還是系統呼叫返回,都使用了 work_pending 和resume_userspace。對於巨集SAVE_ALL來說,這條語句會把將暫存器的值壓入堆疊當中,壓入堆疊的順序對應struct pt_regs ,出棧時,這些值傳遞到struct pt_regs的成員,實現從彙編程式碼向C程式傳遞引數。struct pt_regs可以在arch/x86/include/asm/ptrace.h中檢視。使用者態到核心態需要int 0x80進行中斷,只有生成了中斷向量後才可以切換狀態。中斷處理讓CPU停止當前工作轉為執行系統核心中預設的一些任務,因此必須要對當前CPU執行的任務進行執行現場的保護工作,並對一些其他工作進行檢查,完成呼叫後,再進行檢查,才能執行iret返回。系統內部呼叫涉及CPU架構等內容,不同的CPU對於系統呼叫的彙編具體程式碼是不一樣的。
.macro INTERRUPT_RETURN ; 中斷返回
iret
.endm
.macro SAVE_ALL ; 保護現場
...
.macro RESTORE_INT_REGS
...
.endm
ENTRY(system_call)
SAVE_ALL
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax, PT_EAX(%esp) ; store the return value
syscall exit:
testl $_TIF_ALLWORK_MASK, %ecx # current->work
jne syscall_exit_work
restore_all:
RESTORE_INT_REGS
irq_return:
INTERRUPT_RETURN
ENDPROC(system_call)
syscall_exit_work:
testl $_TIF_WORK_SYSCALL_EXIT, %ecx
jz work_pending
END(syscall_exit_work)
work_pending:
testb $_TIF_NEED_RESCHED, %cl
jz work_notifysig
work_resched:
call schedule
jz restore_all
work_notifysig:
... ; deal with pending signals
END(work_pending)