北京西站地铁几号线,网站设计是不是会要用代码做,国际新闻最新报道,深圳快速网站制作哪家快前面我们学习ARM工作模式中#xff0c;处理器模式切换可以通过软件控制进行切换#xff0c;即修改CPSR模式位#xff0c;但这是在特权模式下#xff0c;当我们处于用户模式下#xff0c;是没有权限实现模式转换的。若想实现模式切换#xff0c;只能由另一种方法来实现处理器模式切换可以通过软件控制进行切换即修改CPSR模式位但这是在特权模式下当我们处于用户模式下是没有权限实现模式转换的。若想实现模式切换只能由另一种方法来实现即通过外部中断或是异常处理过程进行切换。于是ARM指令集中提供了两条产生异常的指令通过这两条指令可以用软件的方法实现异常其中一个就是中断指令SWI 。 一、软件中断 软中断是利用硬件中断的概念用软件方式进行模拟实现从用户模式切换到特权模式并执行特权程序的机制。 硬件中断是由电平的物理特性决定在电平变化时引发中断操作而软中断是通过一条具体指令SWI引发中断操作也就是说用户程序里可以通过写入SWI指令来切换到特权模式当CPU执行到SWI指令时会从用户模式切换到管理模式下执行软件中断处理。由于SWI指令由操作系统提供的API封装起来并且软件中断处理程序也是操作系统编写者提前写好的因此用户程序调用API时就是将操作权限交给了操作系统所以用户程序还是不能随意访问硬件。 软件中断指令Software Interrupt, SWI用于产生软中断实现从用户模式变换到管理模式CPSR保存到管理模式的SPSR中执行转移到SWI向量。在其他模式下也可以使用SWI指令处理器同样切换到管理模式。 1、SWI指令格式如下 SWI{cond} immed_24 其中 immed_24 24位立即数值为从0――16777215之间的整数。 SWI指令后面的24立即数是干什么用的呢用户程序通过SWI指令切换到特权模式进入软中断处理程序但是软中断处理程序不知道用户程序到底想要做什么SWI指令后面的24位用来做用户程序和软中断处理程序之间的接头暗号。通过该软中断立即数来区分用户不同操作执行不同内核函数。如果用户程序调用系统调用时传递参数根据ATPCSC语言与汇编混合编程规则将参数放入R0~R4即可。 2、指令举例 使用SWI指令时通常使用以下两种方法进行参数传递SWI异常处理程序可以提供相关的服务这两种方法均是用户软件协定。SWI异常中断处理程序要通过读取引起软件中断的SWI指令以取得24为立即数。 1、指令中24位的立即数指定了用户请求的服务类型中断服务的参数通过通用寄存器传递。 如下面这个程序产生一个中断号位12 的软中断 MOV R0,#34 设置功能号为34 SWI 12 产生软中断中断号为12 2、指令中的24位立即数被忽略用户请求的服务类型有寄存器R0的值决定参数通过其他的通用寄存器传递。 如下面的例子通过R0传递中断号R1传递中断的子功能号 MOV R0, #12 设置12号软中断 MOV R1, #34 设置功能号为34 SWI 0 下面的例子通过系统调用函数int led_on(int led_no)实现点亮第led_no 个LED灯由于C语言里没有SWI 指令对应的语句因此这儿要用到C语言与汇编混合编程led_on函数里将参数led_no的值传递给R0通过软中断SWI指令切换到软中断管理模式同时R0 软中断方式点亮LED灯用户通过SWI #1指令可以点灯具体点亮哪个灯通过R0保存参数传递如果亮灯成功返回对应LED号。 [cpp] view plaincopy #define __led_on_swi_no 1 // 软中断号1调用管理模式下的do_led_on函数 int led_on(int led_no){ int ret; // 返回值 __asm{ // 由于C程序中没有SWI对应表达式所以使用混合编程 mov r0, led_no // 根据ATPCS规则r0存放第一个参数 swi __led_on_swi_no // 产生SWI软中断中断号为__led_on_swi_no mov ret, r0 // 软中断处理结束取得中断处理返回值传递给ret变量 } return ret; // 将ret返回给调用led_on的语句 } 二、软中断处理 CPU执行到swi xxx执行后产生软件中断由异常处理部分知识可知软中断产生后CPU将强制将PC的值置为异常向量表地址0x08在异常向量表0x08处安放跳转指令b HandleSWI这样CPU就跳往我们自己定义的HandleSWI处执行。 1--保护现场 软中断处理中通过STMFD SP!, {R0-R12, LR} 要保存程序执行现场将R0~R12通用寄存器数据保存在管理模式下SP栈内LR由硬件自动保存软中断指令下一条指令的地址后面利用LR的地址取得SWI指令编码该寄存器值也保存在SP栈内将来处理完毕之后返回 2--获取SWI指令编码 由SWI指令编码知识可知SWI指令低24位保存有软中断号通过LDR R4, [LR, #-4]指令取得SWI指令编码LR为硬件自动保存SWI xxx指令的下一条指令地址LR – 4就是SWI指令地址将其保存在R4寄存器中。通过BIC R4, R4, #0xFF000000 指令将SWI指令高8位清除掉只保留低24位立即数取得指令编码 3--根据SWI指令做出相应操作 根据24位立即数中的软中断号判断用户程序的请求操作。如果24位立即数为1表示led_on系统调用产生的软中断则在管理模式下调用对应的亮灯操作do_led_on。如果24位立即数为2表示led_off系统调用产生的软中断则调用灭灯操作do_led_on根据ATPCS调用规则R0~R3做为参数传递寄存器在软中断处理中没有使用这4个寄存器而是使用R4作为操作寄存器的。 4--返回并恢复现场 执行完系统调用操作之后返回到swi_return在调用对应系统操作时通过LDREQ LR, swi_return设置了返回地址执行返回处理通过LDMIA SP!, {R0-R12, PC}^ 指令将用户寄存器数据恢复到R0~R12将进入软中断处理时保存的返回地址LR的值恢复给PC实现程序返回同时还恢复了状态寄存器。切换回用户模式下程序中继续执行。 [cpp] view plaincopy ; 异常向量表开始 ; 0x00: 复位Reset异常 b Reset ; 0x04: 未定义异常未处理 HandleUndef b HandleUndef ; 0x08: 软件中断异常跳往软件中断处理函数HandleSWI b HandleSWI … … ; 省略其它异常向量和对应处理 … … ;*********************************************************************** ; 软中断处理 ;*********************************************************************** IMPORT do_led_on IMPORT do_led_off HandleSWI STMFD SP!, {R0-R12, LR} ; 保存程序执行现场 LDR R4, [LR, #-4] ; LR - 4 为指令 swi xxx 的地址低24位是软件中断号 BIC R4, R4, #0xFF000000 ; 取得ARM指令24位立即数 CMP R4, #1 ; 判断24位立即数如果为1调用do_led_on系统调用 LDREQ LR, swi_return ; 软中断处理返回地址 LDREQ PC, do_led_on ; 软中断号1对应系统调用处理 CMP R4, #2 ; 判断24位立即数如果为2调用do_led_off系统调用 LDREQ LR, swi_return ; 软中断处理返回地址 LDREQ PC, do_led_off ; 软中断号2对应系统调用处理 MOVNE R0, #-1 ; 没有该软中断号对应函数出错返回-1 swi_return LDMIA SP!, {R0-R12, PC}^ ; 中断返回, ^表示将spsr的值复制到cpsr 其实讲到这会产生一个疑问什么时候需要我们从用户模式切换到管理模式我们应该记得系统调用就是用户态向内核态的切换。 三、系统调用 操作系统的主要功能是为应用程序的运行创建良好的环境保障每个程序都可以最大化利用硬件资源防止非法程序破坏其它应用程序执行环境为了达到这个目的操作系统会将硬件的操作权限交给内核来管理用户程序不能随意使用硬件使用硬件对硬件寄存器进行读写时要先向操作系统发出请求操作系统内核帮助用户程序实现其操作也就是说用户程序不会直接操作硬件而是提供给用户程序一些具备预定功能的内核函数通过一组称为系统调用的system call)的接口呈现给用户系统调用把应用程序的请求传给内核调用相应的内核函数完成所需的处理将处理结果返回给应用程序。 这好比我们去银行取款用户自己的银行帐户不可能随意操作必须要有一个安全的操作流程和规范银行里的布局通常被分成两部分中间用透明玻璃分隔开只留一个小窗口面向用户的是用户服务区工作人员所在区域为内部业务操作区取款时将银行卡或存折通过小窗口交给业务员并且告诉他要取多少钱具体取钱的操作你是不会直接接触的业务员会将银行帐户里减掉取款金额将现金给你。上述操作流程可以很好保护银行系统银行系统的操作全部由业务员来实现用户只能向业务员提出自己的服务请求。银行里的小窗口就类似与操作系统的系统调用接口是将用户请求传递给内核的接口。 操作系统里将用户程序运行在用户模式下并且为其分配可以使用内存空间其它内存空间不能访问内核态运行在特权模式下对系统所有硬件进行统一管理和控制。从前面所学知识可以了解到用户模式下没有权限进行模式切换这也就意味着用户程序不可能直接通过切换模式去访问硬件寄存器如果用户程序试图访问没有权限的硬件会产生异常。这样用户程序被限制起来如果用户程序想要使用硬件时怎么办呢用户程序使用硬件时必须调用操作系统提供的API接口才可以而操作系统API接口通过软件中断方式切换到管理模式下实现从用户模式下进入特权模式。 At91rm9200处理器对应的linux2.4.19内核系统调用对应的软中断定义如下 [cpp] view plaincopy #if defined(__thumb__) //thumb模式 #define __syscall(name) \ push {r7}\n\t \ mov r7, # __sys1(__NR_##name) \n\t \ swi 0\n\t \ pop {r7} #else //arm模式 #define __syscall(name) swi\t __sys1(__NR_##name) \n\t #endif #define __sys2(x) #x #define __sys1(x) __sys2(x) #define __NR_SYSCALL_BASE 0x900000 //此为OS_NUMBER 20运算值 #define __NR_open (__NR_SYSCALL_BASE 5) //0x900005 举一个例子来说:open系统调用,库函数最终会调用__syscall(open),宏展开之后为swi #__NR_open,即,swi #0x900005触发中断,中断号0x900005存放在[lr,#-4]地址中,处理器跳转到arch/arm/kernel/entry-common.S中vector_swi读取[lr,#-4]地址中的中断号,之后查询arch/arm/kernel/entry-common.S中的sys_call_table系统调用表,该表内容在arch/arm/kernel/calls.S中定义,__NR_open在表中对应的顺序号为 __syscall_start: [cpp] view plaincopy .long SYMBOL_NAME(sys_open) //第5个 ... 将sys_call_table[5]中内容传给pc,系统进入sys_open函数,处理实质的open动作 注:用到的一些函数数据所在文件,如下所示 arch/arm/kernel/calls.S声明了系统调用函数 include/asm-arm/unistd.h定义了系统调用的调用号规则 vector_swi定义在arch/arm/kernel/entry-common.S vector_IRQ定义在arch/arm/kernel/entry-armv.S vector_FIQ定义在arch/arm/kernel/entry-armv.S arch/arm/kernel/entry-common.S中对sys_call_table进行了定义: .type sys_call_table, #object ENTRY(sys_call_table) #include calls.S //将calls.S中的内容顺序链接到这里 源程序 [cpp] view plaincopy ENTRY(vector_swi) save_user_regs zero_fp get_scno //将[lr,#-4]中的中断号转储到scno(r7) arm710_bug_check scno, ip #ifdef CONFIG_ALIGNMENT_TRAP ldr ip, __cr_alignment ldr ip, [ip] mcr p15, 0, ip, c1, c0 update control register #endif enable_irq ip str r4, [sp, #-S_OFF]! push fifth arg get_current_task tsk ldr ip, [tsk, #TSK_PTRACE] check for syscall tracing bic scno, scno, #0xff000000 mask off SWI op-code //#define OS_NUMBER 9[entry-header.S] //所以对于上面示例中open系统调用号scno0x900005 //eor scno,scno,#0x900000 //之后scno0x05 eor scno, scno, #OS_NUMBER 20 check OS number //sys_call_table项为calls.S的内容 adr tbl, sys_call_table load syscall table pointer tst ip, #PT_TRACESYS are we tracing syscalls? bne __sys_trace adrsvc al, lr, ret_fast_syscall return address cmp scno, #NR_syscalls check upper syscall limit //执行sys_open函数 ldrcc pc, [tbl, scno, lsl #2] call sys_* routine add r1, sp, #S_OFF 2: mov why, #0 no longer a real syscall cmp scno, #ARMSWI_OFFSET eor r0, scno, #OS_NUMBER 20 put OS number back bcs SYMBOL_NAME(arm_syscall) b SYMBOL_NAME(sys_ni_syscall) not private func /* * This is the really slow path. Were going to be doing * context switches, and waiting for our parent to respond. */ __sys_trace: add r1, sp, #S_OFF mov r0, #0 trace entry [IP 0] bl SYMBOL_NAME(syscall_trace) /* //2007-07-01 gliethttp [entry-header.S] //Like adr, but force SVC mode (if required) .macro adrsvc, cond, reg, label adr\cond \reg, \label .endm //对应反汇编: //add lr, pc, #16 ; lr __sys_trace_return */ adrsvc al, lr, __sys_trace_return return address add r1, sp, #S_R0 S_OFF pointer to regs cmp scno, #NR_syscalls check upper syscall limit ldmccia r1, {r0 - r3} have to reload r0 - r3 ldrcc pc, [tbl, scno, lsl #2] call sys_* routine b 2b __sys_trace_return: str r0, [sp, #S_R0 S_OFF]! save returned r0 mov r1, sp mov r0, #1 trace exit [IP 1] bl SYMBOL_NAME(syscall_trace) b ret_disable_irq .align 5 #ifdef CONFIG_ALIGNMENT_TRAP .type __cr_alignment, #object __cr_alignment: .word SYMBOL_NAME(cr_alignment) #endif .type sys_call_table, #object ENTRY(sys_call_table) #include calls.S