基于arm的linux系统调用分析
2017-09-25 15:44
232 查看
1系统调用的作用
linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用,最简单的如read,write,open等等。用户可以通过系统调用命令在自己的应用程序上调用他们,从某种角度来看,系统调用和普通函数调用非常相似。区别在于系统调用由操作系统核心提供,运行在内核态,而普通函数调用由函数库或用户自己提供,运行于用户态。实际上,很多已经被我们习以为常的C语言标准函数,在linux平台上的实现都是靠系统调用来完成的,所以如果想对系统底层原理作深入的了解,就得掌握各种系统调用时初步的要求。2 系统调用是如何工作的
我们知道一般进程是不能访问内核的,系统调用时用户空间访问内核空间唯一合法的途径。早期linux采用OABI方式传递系统调用的number。现在linux采用新的EABI(Embedded)方式传递系统调用的number。我们主要了解EABI的方式,关于OABI的方式这里就不说了。现在的android编译器arm-linux-androideabi-gcc使用的就是EABI方式。这种新的系统调用方式流程如下:1.将syscall number 存储到r7寄存器中。
2. 执行swi指令跳转到软中断处执行,并从r7中获取syscall number。
注意这里提到的软中断是 supervisor call exception(svc),由于原来名字是software interrupt(swi)一直沿用软中断的叫法。
如果应用程序中有read函数,系统调用里也有read函数,如何从应用程序中找到内核空间中的read函数呢,需要使用swi指令,内核中有很多系统调用函数,read,write,open等等,内核如何知道调用的是哪个函数呢,这里需要用到一个寄存器r7,这个寄存器我们之后还会谈到。
在内核源码中,arch\arm\kernel中有一个文件entry-common.S汇编代码,这段代码是应用程序访问内核的接口,下面给出部分代码。
ENTRY(vector_swi) 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 THUMB( mov r8, sp ) THUMB( store_user_sp_lr r8, r10, S_SP ) @ 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 /* * Get the system call number. */ #if defined(CONFIG_OABI_COMPAT) /*这里采用的是OABI的方式 * If we have CONFIG_OABI_COMPAT then we need to look at the swi * value to determine if it is an EABI or an old ABI call. */ #ifdef CONFIG_ARM_THUMB tst r8, #PSR_T_BIT movne r10, #0 @ no thumb OABI emulation ldreq r10, [lr, #-4] @ get SWI instruction #else ldr r10, [lr, #-4] @ get SWI instruction A710( and ip, r10, #0x0f000000 @ check for SWI ) A710( teq ip, #0x0f000000 ) A710( bne .Larm710bug ) #endif #ifdef CONFIG_CPU_ENDIAN_BE8 rev r10, r10 @ little endian instruction #endif #elif defined(CONFIG_AEABI) /*这里采用EABI的方式 * Pure EABI user space always put syscall number into scno (r7). */ A710( ldr ip, [lr, #-4] @ get SWI instruction ) A710( and ip, ip, #0x0f000000 @ check for SWI ) A710( teq ip, #0x0f000000 ) A710( bne .Larm710bug ) #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 ldreq scno, [lr, #-4] #else /* Legacy ABI only. */ ldr scno, [lr, #-4] @ get SWI instruction A710( and ip, scno, #0x0f000000 @ check for SWI ) A710( teq ip, #0x0f000000 ) A710( bne .Larm710bug ) #endif
在用EABI的方式中,我们可以看出应用系统空间把系统调用编号放在r7寄存器中,如下图
内核需要拿到这个编号,将r7寄存器的值取出来放到scno中。
程序中有句ldrcc pc, [tbl, scno, lsl #2],我们暂且不看其他代码部分,
这句表明内核通过scno取出r7寄存器中的syscall number,
1 该指令将scno逻辑左移2位加到tbl上,把结果位置处所存储的值赋值给PC。 PC = *(tbl+(scno<<2))。
2 tbl 是系统调用table的入口地址,可以把tbl看作char数组头。char tbl[] = {…….};
3 因为scno只是第几个系统调用的意思,不是系统调用的实际地址或者相对地址。
4 而实际每一个系统调用的地址(也就是形象理解的函数名),是 .long x 32位,需要四个字节存储。
5 scno逻辑左移2位,意思为在tbl上偏移scno<<2个字节的位置上,存储着所需的系统调用函数的入口地址。tbl[scno<<2] 存储着所需的系统调用函数的入口地址。
在entry-common.S中调用了sys_call_table,其实就是calls.S的汇编代码,保存着各个系统调用的编号, 内核代码通过这个编号作为偏移来找到相应的系统调用。calls.S部分代码如下,系统调用从0开始,第一个系统调用是sys_reatart_syscall,第二个是sys_exit,当然这些代码是专门由linux内核编写者定义的,如果我们想自己添加自己的系统调用函数,就在这些代码的末尾逐个添加,格式也一般都是相同的(sys_函数名)。例如当你编写一个函数名为mem的时候,在更新calls.S的时候,只要在代码中顺序添加CALL(sys_mem)即可。
/* 0 */ CALL(sys_restart_syscall) CALL(sys_exit) CALL(sys_fork_wrapper) CALL(sys_read) CALL(sys_write) /* 5 */ CALL(sys_open) CALL(sys_close) CALL(sys_ni_syscall) /* was sys_waitpid */ CALL(sys_creat) CALL(sys_link)
在unistd.h文件中,系统调用程序编写基本上靠这个头文件,在每次添加一个系统调用函数的时候,都要更新一次unistd.h和calls.S文件。其中系统调用编号也和call.S中的编号一一对应。代码部分如下,同样也是按照代码中顺序添加我们想要加入的函数名,一般来说格式都为—_NR函数名。
这里在最后需要手动添加#define __NR_mem。
/* * This file contains the system call numbers. */ #define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0) #define __NR_exit (__NR_SYSCALL_BASE+ 1) #define __NR_fork (__NR_SYSCALL_BASE+ 2) #define __NR_read (__NR_SYSCALL_BASE+ 3) #define __NR_write (__NR_SYSCALL_BASE+ 4) #define __NR_open (__NR_SYSCALL_BASE+ 5) #define __NR_close (__NR_SYSCALL_BASE+ 6)
到此,linux的系统调用大概过程就是这样子,具体的笔者并没有去深入,不过现在先理解这么多就可以了,以后随着学习的深入再继续努力。
参考资料:
ARM Linux系统调用详细分析http://blog.csdn.net/liduxun/article/details/48119849#
相关文章推荐
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM]
- [转贴] u-boot 分析 - <节选> [嵌入式Linux系统开发技术详解-基于ARM]
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM] [转贴]
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM]
- Arm linux 系统调用分析
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM]
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM]
- 浅析基于ARM的Linux下的系统调用的实现
- u-boot 分析- [嵌入式Linux系统开发技术详解-基于ARM]2
- ARM Linux系统调用详细分析
- u-boot 分析 - [嵌入式Linux系统开发技术详解-基于ARM] [转载]
- [linux内核]ARM-Linux系统调用
- 如何来实现一个Linux内核的系统调用(基于tiny4412开发板)
- 基于ARM+linux的嵌入式系统设计
- ARM Linux系统调用的原理
- 【转】Linux-2.6.25 select系统调用源码分析
- linux内核中断、异常、系统调用的分析以及实践
- 基于Linux x86_64系统编译arm-gcc-4.8: arm-none-eabi-gcc
- 基于S3C2410-ARM-Linux静态映射分析
- 基于arm的Linux的启动分析(至start_kernel)