Linux 系统调用如何从用户空间到内核空间再回到用户空间?
我研究了一些文章,在这些文章中,我得到了如下信息:像open()这样的系统调用调用glibc中的包装函数,然后引发了一个陷阱,它将上下文从用户空间切换到内核空间,然后使用cpu寄存器调用内核空间中的系统调用参数/参数Linux 系统调用如何从用户空间到内核空间再回到用户空间?,linux,linux-kernel,linux-device-driver,embedded-linux,Linux,Linux Kernel,Linux Device Driver,Embedded Linux,我研究了一些文章,在这些文章中,我得到了如下信息:像open()这样的系统调用调用glibc中的包装函数,然后引发了一个陷阱,它将上下文从用户空间切换到内核空间,然后使用cpu寄存器调用内核空间中的系统调用参数/参数 但是我想我还是缺少了系统调用的一步一步的过程或详细的顺序。如果人们能够提供将ARM arch作为参考的步骤,那就太好了。提前感谢。软件中断异常用于在ARM情况下调用系统调用。它将执行其地址存储在物理地址0x08的函数 Syscall包装器库函数连接到特定于体系结构的Syscall实
但是我想我还是缺少了系统调用的一步一步的过程或详细的顺序。如果人们能够提供将ARM arch作为参考的步骤,那就太好了。提前感谢。软件中断异常用于在ARM情况下调用系统调用。它将执行其地址存储在物理地址
0x08
的函数
Syscall包装器库函数连接到特定于体系结构的Syscall实现(检查sysdeps/unix
libc源代码目录)。
在本例中,将执行来自sysdeps/unix/sysv/linux/arm/syscall.S
文件的syscall。在此函数中,它将系统调用号存储在R7
和R0-R6
用于向系统调用发送参数
示例程序集代码:
mov r7, #SYSCALL NO
mov r0, #ARG1
mov r1, #ARG2
swi 0x0
生成软件异常时,将调用vector\u swi()
。此函数从R7获取系统调用号,
从sys\u call\u表
中查找并执行注册的函数地址
有关实现详细信息,请检查以下内核文件:
软件中断异常用于在ARM情况下调用系统调用。它将执行其地址存储在物理地址
0x08
的函数
Syscall包装器库函数连接到特定于体系结构的Syscall实现(检查sysdeps/unix
libc源代码目录)。
在本例中,将执行来自sysdeps/unix/sysv/linux/arm/syscall.S
文件的syscall。在此函数中,它将系统调用号存储在R7
和R0-R6
用于向系统调用发送参数
示例程序集代码:
mov r7, #SYSCALL NO
mov r0, #ARG1
mov r1, #ARG2
swi 0x0
生成软件异常时,将调用vector\u swi()
。此函数从R7获取系统调用号,
从sys\u call\u表
中查找并执行注册的函数地址
有关实现详细信息,请检查以下内核文件:
系统调用的处理分为两部分
W(ldr) pc, __vectors_start + 0x1000
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
mrs lr, spsr
...
and lr, lr, #0x0f
...
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
...
.word vector_swi
以下是异常的公共处理程序条目:
[文件:arch\arm\kernel\entry armv.S]
W(ldr) pc, __vectors_start + 0x1000
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
mrs lr, spsr
...
and lr, lr, #0x0f
...
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
...
.word vector_swi
2b。在完成此公共处理程序时,r0、lr和spsr分别保存在堆栈上的[SP]、[SP+4]和[SP+8]地址处
[文件:arch\arm\kernel\entry armv.S]
W(ldr) pc, __vectors_start + 0x1000
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
mrs lr, spsr
...
and lr, lr, #0x0f
...
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
...
.word vector_swi
2c。然后,公共处理程序将控制器转移/分支到SVC特定处理程序:
[文件:arch\arm\kernel\entry armv.S]
W(ldr) pc, __vectors_start + 0x1000
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
mrs lr, spsr
...
and lr, lr, #0x0f
...
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
...
.word vector_swi
在公共处理程序的末尾,控制器被传输到SVC处理程序vector_swi
2d。现在让我们看看如何在SVC处理程序中处理syscall。
按以下顺序在新帧上推进SP后,调用者的上下文保存为:
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
(r0-r12,sp,lr)_usr_模式,lr_exp(在这种情况下exp=svc)
通过存储r0-r12、sp、lr、pc的该上下文值,该上下文将用于从SVC模式返回用户模式
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0 - r12
ARM( add r8, sp, #S_PC )
ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
...
mrs r8, spsr @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC] @ Save calling PC
2e。系统调用号是根据过程调用标准获取的,通常存储在scno(r7寄存器)中
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
2f。然后,sys_call_表的地址存储在tbl寄存器(r8)中
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
sys_call_表包含根据其编号的syscall列表。
文件call.S包含该列表
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
2g。然后,通过使用syscall no的偏移量分支到sys_call_表,将涉及正确的syscall处理程序。
reurn地址设置在标签“ret\u fast\u syscall”处
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
2h。然后,以这样的方式恢复用户模式寄存器,即从步骤2d中存储的值存储(r0-r12、sp、lr、pc)_usr_模式。
这在宏“restore\u user\u regs”中完成
[文件:arch\arm\kernel\entry common.S]
ENTRY(vector_swi)
...
elif defined(CONFIG_AEABI)
/*
* Pure EABI user space always put syscall number into scno (r7).
*/
#elif defined(CONFIG_ARM_THUMB)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
...
adr tbl, sys_call_table @ load syscall table pointer
ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...
adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
...
ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...
[文件:arch\arm\kernel\entry header.S]
macro restore_user_regs, fast = 0, offset = 0
mov r2, sp
...
.if \fast
ldmdb sp, {r1 - r12} @ get calling r1 - r12
.else
ldmdb sp, {r0 - r12} @ get calling r0 - r12
.endif
add sp, sp, #S_FRAME_SIZE - S_SP
movs pc, lr @ return & move spsr_svc into cpsr
.endm
系统调用的处理分为两部分