當(dāng)發(fā)生系統(tǒng)調(diào)用、產(chǎn)生異常,外設(shè)發(fā)生中斷等事件時,會發(fā)生用戶棧和內(nèi)核棧之間的切換,本文從系統(tǒng)調(diào)用角度分析用戶棧與內(nèi)核棧的切換。
系統(tǒng)調(diào)用的演變
x86 的系統(tǒng)調(diào)用經(jīng)歷了 int / iret 到 sysenter / sysexit 再到 syscall / sysret 實(shí)現(xiàn)方式的轉(zhuǎn)變,關(guān)于具體的演化和區(qū)別、系統(tǒng)調(diào)用的其他細(xì)節(jié)等將在以后的系統(tǒng)調(diào)用專欄里分析。本文從系統(tǒng)調(diào)用最原始的int 0x80開始分析用戶棧與內(nèi)核棧的切換,重點(diǎn)看系統(tǒng)調(diào)用過程用戶棧與內(nèi)核棧切換的過程中的一些細(xì)節(jié)。
系統(tǒng)調(diào)用-分析從用戶棧切換內(nèi)核棧
內(nèi)核SYSCALL 入口代碼在entry_64.S中:
//arch/x86/entry/entry_64.S
ENTRY(entry_SYSCALL_64)
UNWIND_HINT_EMPTY
/* Interrupts are off on entry. */
swapgs
// 將用戶棧偏移保存到 per-cpu 變量 rsp_scratch 中
movq %rsp, PER_CPU_VAR(rsp_scratch)
// 切換到進(jìn)程內(nèi)核棧
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
/* 在棧中倒序構(gòu)建 struct pt_regs */
pushq $__USER_DS /* pt_regs- >ss */
pushq PER_CPU_VAR(rsp_scratch) /* pt_regs- >sp */
pushq %r11 /* pt_regs- >flags */
pushq $__USER_CS /* pt_regs- >cs */
pushq %rcx /* pt_regs- >ip */
GLOBAL(entry_SYSCALL_64_after_hwframe)
//rax 保存著系統(tǒng)調(diào)用號
pushq %rax /* pt_regs- >orig_ax */
PUSH_AND_CLEAR_REGS rax=$-ENOSYS
TRACE_IRQS_OFF
/* 保存參數(shù)到寄存器,調(diào)用do_syscall_64函數(shù) */
movq %rax, %rdi
movq %rsp, %rsi
call do_syscall_64 /* returns with IRQs disabled */
上面的匯編指令中先將當(dāng)前用戶棧(用戶空間棧頂)記錄在CPU獨(dú)占變量區(qū)域里(PER_CPU變量),如下所示:
movq %rsp, PER_CPU_VAR(rsp_scratch)
然后將CPU獨(dú)占區(qū)域里記錄的內(nèi)核棧頂放入rsp/esp寄存器
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
就是這么簡潔,**上面兩句匯編:就 將用戶棧頂保存在了當(dāng)前CPU的rsp_scratch這樣一個PER_CPU變量里,完成了用戶棧的保存 ,然后 將當(dāng)前內(nèi)核棧的地址存放到當(dāng)前棧指針寄存器中 ,**那么此時棧寄存器指向的就是內(nèi)核棧的棧頂,由此優(yōu)雅、完美地完成了用戶棧到內(nèi)核棧的切換!
接下來所有的壓棧操作都是在內(nèi)核棧里操作了,依次將用戶空間寄存器壓棧,此時也是往內(nèi)核棧push的, 內(nèi)核使用struct pt_regs初始化內(nèi)核棧,也就是通過push保存寄存器的值 (將用戶棧信息:用戶調(diào)用的系統(tǒng)調(diào)用號、參數(shù)、代碼段地址、數(shù)據(jù)段地址等以struct pt_regs形式壓入棧) ,形成一個pt_regs結(jié)構(gòu) ,如下圖(源于上篇文章中的分析):
在棧中順序固定且倒序壓棧(在x86_64中,內(nèi)核棧rbx rbp r12 r13 r14 r15不是必須保存的項(為了訪問不越界相應(yīng)空間必須保留),根據(jù)需要保存,linux后續(xù)版本采取都保存方式),其中rax保存系統(tǒng)調(diào)用號
//rax 保存著系統(tǒng)調(diào)用號
pushq %rax /* pt_regs- >orig_ax */
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1383瀏覽量
40438 -
Linux
+關(guān)注
關(guān)注
87文章
11350瀏覽量
210477
發(fā)布評論請先 登錄
相關(guān)推薦
評論