Arm linux 系统调用分析
2014-01-21 13:49
351 查看
Arm linux 系统调用分析
概述
操作系统为在用户态运行的进程与硬件设备进行交互,提供操作系统的系统服务,提供了一组接口。在应用程序和硬件之间,内核提供的系统服务设置一个额外层具有很多优点。
首先,这使得编程更加容易,把用户从学习硬件设备的低级编程特性中解放出来。
其次,这极大地提高了系统的安全性,因为内核在试图满足某个请求之前在接口级就可以检查这种请求的正确性。
最后, 更重要的是这些接口使得程序具有可移植性,因为只要内核所提供的一组接口相同,那么在任一内核之上就可以正确地编译和执行程序。
![](http://blog.chinaunix.net/attachment/201211/10/28236237_1352554463CYeI.jpg)
ARM Linux系统利用SWI指令来从用户空间进入内核空间,还是先让我们了解下这个SWI指令吧。SWI指令用于产生软件中断,从而实现从用户模式到管理模 式的变换,CPSR保存到管理模式的SPSR,执行转移到SWI向量。在其他模式下也可使用SWI指令,处理器同样地切换到管理模式。指令格式如下:
SWI{cond} immed_24
其中:
immed_24 24位立即数,值为从0——16777215之间的整数。
使用SWI指令时,通常使用以下两种方法进行参数传递,SWI异常处理程序可以提供相关的服务,这两种方法均是用户软件协定。
1) 指令中24位的立即数指定了用户请求的服务类型,参数通过通用寄存器传递。SWI异常处理程序要通过读取引起软件中断的SWI指令,以取得24为立即数。如:
MOV R0,#34
SWI 12
2) 指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其他的通用寄存器传递。如:
MOV R0, #12
MOV R1, #34
SWI 0
Arm Linux内核处理系统调用
在分析arm中断处理过程中我们提到过arm架构中的异常处理向量表,其中有一个地址就是处理swi软件中断的入口。
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0:
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset
.globl __vectors_end:
__vectors_end:
Cpu执行上面红色指令之后跳转到entry-common.s执行vector_swi处的指令,该函数就是内核中处理系统调用的入口。
在用户态调用swi指令,cpu从user状态切换到svc状态。首先需要执行的就是保存cpu现场,这样才能在陷入内核态执行了相应的系统调用处理过程之后,恢复cpu用户态的状态,重新执行用户态进程。
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0 - r12
add r8, sp, #S_PC
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
str r8, [sp, #S_PSR] @ Save CPSR
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
zero_fp
BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
BLANK();
ldr r10, [lr, #-4] @ get SWI instruction
之后,将swi指令保存在r10寄存器中
enable_irq
get_thread_info tsk
adr tbl, sys_call_table @ load syscall table pointer
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
开启中断,将thread info结构的指针保存在tsk,将sys_call_table的地址保存在tbl中,将thread info结构中的flag保存ip中。
scno .req r7 @ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info
bics r10, r10, #0xff000000
eorne scno, r10, #__NR_OABI_SYSCALL_BASE
ldrne tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)
bic scno, scno, #0xff000000 @ mask off SWI op-code
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#endif
检查swi命令中带的中断号是不是为0,如果不等于零说明是是old ABI方式调用的系统调用,这时将sys_oabi_call_table的地址载入到tbl中,同时得到系统调用的中断号,保存在scno中。如果swi指令中带点中断号为0,说明系统调用是利用scno寄存器传递中断号的。利用异或指令,将__NR_SYSCALL_BASE清除掉,得到实际的系统请求号(系统调用号定义为__NR_SYSCALL_BASE+x)。
stmdb sp!, {r4, r5} @ push fifth and sixth args
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
bne __sys_trace
如果进程带有syscall trace标志就调用sys_trace,这处应该是作为调试用的。
cmp scno, #NR_syscalls @ check upper syscall limit
adr lr, ret_fast_syscall @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
如果系统调用号,不大于NR_syscalls,就跳转到系统调用函数去执行ldrcc pc, [tbl, scno, lsl #2]
总结
Arm系统调用的简单概述。首先是用户态执行swi指令,swi指令使得cpu陷入svc状态,并跳转到固定地址去执行系统调用处理过程。用户态通过两种方法传递给内核执行系统调用的系统调用号。内核执行系统调用处理过程,首先保存cpu现场,之后会获取到系统调用号,以系统调用地址表的基地址,加系统调用号做偏移,跳转到相应的系统调用例程做相应的处理。
概述
操作系统为在用户态运行的进程与硬件设备进行交互,提供操作系统的系统服务,提供了一组接口。在应用程序和硬件之间,内核提供的系统服务设置一个额外层具有很多优点。
首先,这使得编程更加容易,把用户从学习硬件设备的低级编程特性中解放出来。
其次,这极大地提高了系统的安全性,因为内核在试图满足某个请求之前在接口级就可以检查这种请求的正确性。
最后, 更重要的是这些接口使得程序具有可移植性,因为只要内核所提供的一组接口相同,那么在任一内核之上就可以正确地编译和执行程序。
![](http://blog.chinaunix.net/attachment/201211/10/28236237_1352554463CYeI.jpg)
ARM Linux系统利用SWI指令来从用户空间进入内核空间,还是先让我们了解下这个SWI指令吧。SWI指令用于产生软件中断,从而实现从用户模式到管理模 式的变换,CPSR保存到管理模式的SPSR,执行转移到SWI向量。在其他模式下也可使用SWI指令,处理器同样地切换到管理模式。指令格式如下:
SWI{cond} immed_24
其中:
immed_24 24位立即数,值为从0——16777215之间的整数。
使用SWI指令时,通常使用以下两种方法进行参数传递,SWI异常处理程序可以提供相关的服务,这两种方法均是用户软件协定。
1) 指令中24位的立即数指定了用户请求的服务类型,参数通过通用寄存器传递。SWI异常处理程序要通过读取引起软件中断的SWI指令,以取得24为立即数。如:
MOV R0,#34
SWI 12
2) 指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其他的通用寄存器传递。如:
MOV R0, #12
MOV R1, #34
SWI 0
Arm Linux内核处理系统调用
在分析arm中断处理过程中我们提到过arm架构中的异常处理向量表,其中有一个地址就是处理swi软件中断的入口。
.globl __vectors_start
__vectors_start:
swi SYS_ERROR0:
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset
.globl __vectors_end:
__vectors_end:
Cpu执行上面红色指令之后跳转到entry-common.s执行vector_swi处的指令,该函数就是内核中处理系统调用的入口。
在用户态调用swi指令,cpu从user状态切换到svc状态。首先需要执行的就是保存cpu现场,这样才能在陷入内核态执行了相应的系统调用处理过程之后,恢复cpu用户态的状态,重新执行用户态进程。
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0 - r12
add r8, sp, #S_PC
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
str r8, [sp, #S_PSR] @ Save CPSR
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
zero_fp
BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
BLANK();
ldr r10, [lr, #-4] @ get SWI instruction
之后,将swi指令保存在r10寄存器中
enable_irq
get_thread_info tsk
adr tbl, sys_call_table @ load syscall table pointer
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
开启中断,将thread info结构的指针保存在tsk,将sys_call_table的地址保存在tbl中,将thread info结构中的flag保存ip中。
scno .req r7 @ syscall number
tbl .req r8 @ syscall table pointer
why .req r8 @ Linux syscall (!= 0)
tsk .req r9 @ current thread_info
bics r10, r10, #0xff000000
eorne scno, r10, #__NR_OABI_SYSCALL_BASE
ldrne tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)
bic scno, scno, #0xff000000 @ mask off SWI op-code
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#endif
检查swi命令中带的中断号是不是为0,如果不等于零说明是是old ABI方式调用的系统调用,这时将sys_oabi_call_table的地址载入到tbl中,同时得到系统调用的中断号,保存在scno中。如果swi指令中带点中断号为0,说明系统调用是利用scno寄存器传递中断号的。利用异或指令,将__NR_SYSCALL_BASE清除掉,得到实际的系统请求号(系统调用号定义为__NR_SYSCALL_BASE+x)。
stmdb sp!, {r4, r5} @ push fifth and sixth args
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
bne __sys_trace
如果进程带有syscall trace标志就调用sys_trace,这处应该是作为调试用的。
cmp scno, #NR_syscalls @ check upper syscall limit
adr lr, ret_fast_syscall @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
如果系统调用号,不大于NR_syscalls,就跳转到系统调用函数去执行ldrcc pc, [tbl, scno, lsl #2]
总结
Arm系统调用的简单概述。首先是用户态执行swi指令,swi指令使得cpu陷入svc状态,并跳转到固定地址去执行系统调用处理过程。用户态通过两种方法传递给内核执行系统调用的系统调用号。内核执行系统调用处理过程,首先保存cpu现场,之后会获取到系统调用号,以系统调用地址表的基地址,加系统调用号做偏移,跳转到相应的系统调用例程做相应的处理。
相关文章推荐
- ARM Linux系统调用详细分析
- 基于arm的linux系统调用分析
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM] [转贴]
- Linux操作系统分析(4)- 更新内核与添加系统调用
- Linux系统调用的实现机制分析
- linux中mmap系统调用原理分析与实现
- linux内核分析之系统调用
- ARM-Linux系统调用(http://my.oschina.net/raybin/blog/100379)
- ARM linux系统调用的实现原理
- Linux添加新的系统调用(for arm )
- Linux的IO系统常用系统调用及分析
- Linux--Sys_Read系统调用过程分析 http://blog.csdn.net/guopeixin/article/details/5962489
- ARM Linux系统调用的原理
- Arm linux 内核移植及系统初始化过程分析
- ARM linux系统调用的实现原理
- Linux系统调用的实现机制分析
- ARM-Linux系统调用流程
- Linux内核分析——扒开系统调用的三层皮(下)
- ARM Linux系统调用的原理
- linux-kernel 3.5.3Tcp系统调用,源码分析7-listen系统调用