X86_64处理器系统调用机制在linux上的实现
硬件机制
X86 64位flat模式提供了快速系统调用硬件机制。使用syscall指令触发系统调用,CPU从用户态(Ring3)切换到特权态(Ring0),使用sysret指令,CPU从内核态切换到用户态。
注意:sysret指令和iret指令是CPU从内核态切换到用户态的两种方式
- syscall 指令
执行syscall
指令,发生系统调用时,CPU硬件执行以下动作:
- 把MSR_LSTAR寄存器中的值加载到RIP寄存器,并把当前程序运行的下一条指令(即
syscall
指令的下一条指令)保存在RCX寄存器中 - 把当前的RFLAGS寄存器的值保存在R11寄存器,并使用MSR_FMASK寄存器的值mask当前RFLAGS的值。一般通过这种方式关闭中断,保证进入系统调用后,CPU的中断时关闭的
- 把MSR_STAR寄存器的
SYSCALL CS and SS
分别加载到CPU的CS和SS段寄存器,同时更新CS和SS的不可见部分。
注意:syscall指令不会更新RSP寄存器的值,由OS去负责切换程序栈。
- sysret 指令
执行sysret指令,CPU从内核特权态(Ring0)返回到用户态(Ring3),从syscall的指令的下一条指令处继续执行。CPU硬件执行以下动作:
- 把RCX寄存器中的值加载到RIP寄存器
- 把R11寄存器中的值加载到rflags寄存器
- 把MSR_STAR寄存器中的
SYSRET CS and SS
分别加载到CS和SS段寄存器。
注意:sysret指令不修改RSP寄存器的值,OS负责切换程序栈。
- MSR 寄存器
linux kernel中syscall的处理
linux kernel里面系统调用的入口函数是entry_SYSCALL_64
,在arch/x86/entry/entry_64.S
中实现。该函数被保存在MSR_LSTAR寄存器中。
系统调用初始化
在系统调用初始化函数里面:
- 将系统调用入口函数
entry_SYSCALL_64
写入MSR_LSTAR寄存器中 - 初始化MSR_STAR寄存器中的
SYSCALL CS and SS
- 初始化MSR_SFMASK中的
SYSCALL Flag Mask
,系统调用发生时,RFLAGS中的这些位将被清零