接下來:call
do_syscall_64,進入do_syscall_64函數:
__visible void do_syscall_64(struct pt_regs *regs)
{
struct thread_info *ti = current_thread_info();
unsigned long nr = regs- >orig_ax;
enter_from_user_mode();
local_irq_enable();
if (READ_ONCE(ti- >flags) & _TIF_WORK_SYSCALL_ENTRY)
nr = syscall_trace_enter(regs);
/*
* NB: Native and x32 syscalls are dispatched from the same
* table. The only functional difference is the x32 bit in
* regs- >orig_ax, which changes the behavior of some syscalls.
*/
if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) {
regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
regs- >di, regs- >si, regs- >dx,
regs- >r10, regs- >r8, regs- >r9);
}
syscall_return_slowpath(regs);
}
上述函數的主邏輯很簡單:
1、 通過之前保存下來的pt_regs(往內核棧中格式化壓入的),獲取用戶傳入的系統調用號nr,系統調用號保存在了regs->orig_ax:
unsigned long nr = regs- >orig_ax;
2、 通過系統調用號nr,執行對應的回調函數,sys_call_table是函數指針數組,不同nr對應不同系統調用對應的函數。其中regx->di、regx->si、regs->dx、regs->r10、regs->r8、regs->r9分別是之前保存到內核棧(以struct
pt_regs格式化)保存到pt_regs中的,對應著用戶傳入該系統調用的參數1~6:
if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) {
regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
regs- >di, regs- >si, regs- >dx,
regs- >r10, regs- >r8, regs- >r9);
}
以上就完成了用戶調用系統調用,并從用戶棧切換到內核棧,并執行到系統調用號對應函數的過程。 具體的系統調用相關細節將在以后系統調用相關文章中分析。
系統調用-分析從內核棧切換用戶棧
上面分析到了執行系統調用對應的函數,如下所示,并將返回值保存在regs->ax中了
regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
regs- >di, regs- >si, regs- >dx,
regs- >r10, regs- >r8, regs- >r9);
函數執行到do_syscall_64->syscall_return_slowpath(regs),開始為返回用戶態做準備.
并最終回到系統調用 內核SYSCALL 入口:ENTRY(entry_SYSCALL_64)->return_from_SYSCALL_64,繼續完成系統調用返回工作,并切換用戶棧與內核棧,使用struct pt_regs恢復用戶態寄存器值。
總之
用戶棧——>內核棧: cpu保存用戶當前堆棧信息保存到內核的棧中(恢復時用到),然后將cpu指向內核堆棧,去執行內核代碼。完成用用戶棧到內核棧轉換。
內核?!?用戶棧: 再切換到內核堆棧前,將用戶堆棧信息壓入到內核棧中,內核函數執行完回退棧幀,會將用戶的堆棧信息POP出棧,然后cpu堆棧寄存器就知道怎么回去了,返回的用戶程序中斷的地方繼續執行。
-
內核
+關注
關注
3文章
1381瀏覽量
40362 -
Linux
+關注
關注
87文章
11336瀏覽量
210097
發布評論請先 登錄
相關推薦
評論