用户态与内核态之间的切换
1、用户态与内核态之间通过系统调用接口完成交流,系统调用接口的所有种类:
syscalls.h - include/linux/syscalls.h - Linux source code (v5.16.3) - Bootlin
当执行程序的时候可以通过strace命令查看程序具体使用了哪些系统调用接口。
strace工具_sydyh43的博客-CSDN博客_strace工具
2、用户态与内核态之间的切换
静态编译用户态的代码,完成反汇编。否则需要反汇编*.so动态库。
arm-linux-gcc open.c -o open --staticarm-linux-objdump -D open > open.txt
285f0: e59f7074 ldr r7, [pc, #116] ; 2866c <__libc_open+0x128>285f4: e1a06000 mov r6, r0285f8: e59d1004 ldr r1, [sp, #4]285fc: e59d2024 ldr r2, [sp, #36] ; 0x2428600: e59d3000 ldr r3, [sp]28604: e3e00063 mvn r0, #99 ; 0x6328608: ef000000 svc 0x00000000
将系统调用号存放到r7寄存器中。
2.1、用户态进入到内核态
通过svc汇编指令触发异常,从而从用户态进入到内核态,跳转到vector_swi。参考ARM处理器的异常模式_sydyh43的博客-CSDN博客
/*=============================================================================* SWI handler*-----------------------------------------------------------------------------*/.align 5
ENTRY(vector_swi)
#ifdef CONFIG_CPU_V7Mv7m_exception_entry
#elsesub sp, sp, #PT_REGS_SIZE @ PT_REGS_SIZE=72 开辟72B,保存参数stmia sp, {r0 - r12} @ Calling r0 - r12ARM( add r8, sp, #S_PC )ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lrTHUMB( mov r8, sp )THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lrmrs saved_psr, spsr @ called from non-FIQ mode, so ok.TRACE( mov saved_pc, lr )str saved_pc, [sp, #S_PC] @ Save calling PCstr saved_psr, [sp, #S_PSR] @ Save CPSRstr r0, [sp, #S_OLD_R0] @ Save OLD_R0
#endifreload_current r10, ip @ 保存跳转过来的信息,返回用zero_fp @ 清除frame pointer,表示栈顶alignment_trap r10, ip, __cr_alignmentasm_trace_hardirqs_on save=0enable_irq_notracect_user_exit save=0/** Get the system call number.*/#if defined(CONFIG_OABI_COMPAT)/** 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_THUMBtst saved_psr, #PSR_T_BITmovne r10, #0 @ no thumb OABI emulationUSER( ldreq r10, [saved_pc, #-4] ) @ get SWI instruction
#elseUSER( ldr r10, [saved_pc, #-4] ) @ get SWI instruction
#endifARM_BE8(rev r10, r10) @ little endian instruction#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 saved_psr, #PSR_T_BIT @ this is SPSR from save_user_regsaddne scno, r7, #__NR_SYSCALL_BASE @ put OS number inUSER( ldreq scno, [saved_pc, #-4] )#else/* Legacy ABI only. */USER( ldr scno, [saved_pc, #-4] ) @ get SWI instruction
#endif/* saved_psr and saved_pc are now dead */uaccess_disable tblget_thread_info tsk @ 8K地址对齐,struct thread_info基地址存放在tsk(r9)adr tbl, sys_call_table @ load syscall table pointer 系统调用表存放在tbl(r8)#if defined(CONFIG_OABI_COMPAT)/** If the swi argument is zero, this is an EABI call and we do nothing.** If this is an old ABI call, get the syscall number into scno and* get the old ABI syscall table address.*/bics r10, r10, #0xff000000strne r10, [tsk, #TI_ABI_SYSCALL]streq scno, [tsk, #TI_ABI_SYSCALL]eorne scno, r10, #__NR_OABI_SYSCALL_BASEldrne tbl, =sys_oabi_call_table
#elif !defined(CONFIG_AEABI)bic scno, scno, #0xff000000 @ mask off SWI op-codestr scno, [tsk, #TI_ABI_SYSCALL]eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#elsestr scno, [tsk, #TI_ABI_SYSCALL]
#endif/** Reload the registers that may have been corrupted on entry to* the syscall assembly (by tracing or context tracking.)*/TRACE( ldmia sp, {r0 - r3} )local_restart:ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing; thread_info->flags保存到r10中stmdb sp!, {r4, r5} @ push fifth and sixth args;arm处理器r0~r3保存入参tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls?;系统调用tracingbne __sys_trace @ _TIF_SYSCALL_WORK不为零(置位)跳转系统调用tracing分支@ invoke_syscall宏 四个入参tbl(r8), scno(r7), r10, __ret_fast_syscall返回函数@ 执行系统调用接口,结束后返回执行__ret_fast_syscallinvoke_syscall tbl, scno, r10, __ret_fast_syscall @ 调用系统接口add r1, sp, #S_OFF
2: cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)eor r0, scno, #__NR_SYSCALL_BASE @ put OS number backbcs arm_syscallmov why, #0 @ no longer a real syscallb sys_ni_syscall @ not private func#if defined(CONFIG_OABI_COMPAT) || !defined(CONFIG_AEABI)/** We failed to handle a fault trying to access the page* containing the swi instruction, but we're not really in a* position to return -EFAULT. Instead, return back to the* instruction and re-enter the user fault handling path trying* to page it in. This will likely result in sending SEGV to the* current task.*/
9001:sub lr, saved_pc, #4str lr, [sp, #S_PC]get_thread_info tskb ret_fast_syscall
#endif
ENDPROC(vector_swi)
svc 模式下的 sp 和 user 模式下的 sp 并不是同一个,因此无法使用栈直接进行传递,需要将所有的参数保存在通用寄存器中进行传递。用户态和内核态下的栈空间地址区域
用户态的栈地址cat /proc/pid/maps查看
内核态的栈地址,只需要获取struct thread_info结构体的地址current_thread_info()
最终在
invoke_syscall tbl, scno, r10, __ret_fast_syscall
完成系统调用接口的使用。其中scno(r7)就是从用户态传下来的系统调用号。
2.2、内核态返回到用户态
当执行完系统调用接口后,需要从内核态返回到用户态,跳转到__ret_fast_syscall:标签,根据thread_info->flags值(thread_info.h - arch/arm/include/asm/thread_info.h - Linux source code (v5.16.7) - Bootlin)跳转到slow_work_pending,在do_work_pending函数中完成调度schedule和信号处理do_signal。
slow_work_pending:mov r0, sp @ 'regs'mov r2, why @ 'syscall'bl do_work_pending @ 执行schedule和do_signal的时机cmp r0, #0beq no_work_pending @ 弹栈,返回用户态movlt scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE)ldmia sp, {r0 - r6} @ have to reload r0 - r6b local_restart @ ... and off we go
ENDPROC(ret_fast_syscall)
等do_work_pending结束,跳转到no_work_pending,进入restore_user_regs,弹栈返回用户态
no_work_pending:asm_trace_hardirqs_on save = 0/* perform architecture specific actions before user return */arch_ret_to_user r1, lrct_user_enter save = 0restore_user_regs fast = 0, offset = 0
用户态与内核态之间的切换相关推荐
- 用户态和内核态之间的切换
用户态和内核态之间的切换 切换方式 从用户态到内核态切换可以通过三种方式,或者说会导致从用户态切换到内核态的操作: 系统调用,这个上面已经讲解过了,在我公众号之前的文章也有讲解过.其实系统调用本身就是 ...
- 操作系统用户态和内核态之间的切换过程
操作系统用户态和内核态之间的切换过程 1. 用户态和内核态的概念区别 究竟什么是用户态,什么是内核态,这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重 ...
- 用户态与内核态之间切换详解
用户空间和内核空间 用户程序有用户态和内核态两种状态.用户态就是执行在用户空间中,不能直接执行系统调用.必须先切换到内核态,也就是系统调用的相关数据信息必须存储在内核空间中,然后执行系统调用. 操作硬 ...
- 操作系统 | 用户态和内核态的切换(中断、系统调用与过程(库函数)调用)
文章目录 中断 过程调用 系统调用 过程调用和系统调用的区别 中断 用户态.内核态之间的切换是怎么实现的? 用户态→内核态 是通过中断实现的.并且 中断是唯一途径 . 核心态→用户态 的切换是通过执行 ...
- 系统调用原理与用户态以及内核态相互切换过程,以linux系统为主
1. 讲系统调用前需要清楚的几个基本概念 1.1 内核态与用户态 内核态:当CPU执行内核的代码(CPU堆栈指针指向内核堆栈)时,我们就称此时处于内核态,内核态的代码可以使用特权指令,这些指令可以控制 ...
- 用户态和内核态的切换
用户态和内核态的切换 我们经常说,一个程序依赖DMA设备来减少用户态和内核态的切换次数,以此来提升性能.那么对于它的细节是如何进行的呢?本篇,博主与你一起研究一下用户态和内核态的划分,以及这件事情的前 ...
- Linux系统编程33:进程信号之详解信号的捕捉过程,用户态和内核态及其切换,sigaction和signal
文章目录 (1)用户态和内核态 (2)用户态和内核态的切换 (3)内核是如何实现信号的捕捉 (4)sigaction (1)用户态和内核态 我们说过,每个Linux进程有4GB的地址空间 其中0-3G ...
- 用户态和内核态的切换耗费时间的原因
用户态和内核态的切换耗费时间的原因 总的来说,就是线程切换或者加锁解锁都是因为需要用户态和内核态的切换,从而导致的开销大. 应用程序的执行需要依托内核提供的资源,包括CPU.存储.IO等,因此内核提供 ...
- 进程用户态和内核态及其切换过程
1.进程的堆栈 内核在创建进程的时候,会为进程创建相应的堆栈.每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存在于内核空间.当进程在用户空间运行时,CPU堆栈指针寄存器里面的内容是用户堆 ...
- 进程用户态和内核态及其切换过程(转)
1.进程的堆栈 内核在创建进程的时候,会为进程创建相应的堆栈.每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存在于内核空间.当进程在用户空间运行时,CPU堆栈指针寄存器里面的内容是用户堆 ...
最新文章
- 使用 Matplotlib 这么久,竟不知道数据可以动起来
- mac -- 安装OpenCV
- FSLib.Extension库
- linux为用户配置java环境变量
- sql添加列,删除列,修改列
- 【蓝桥杯】基础练习 十六进制转八进制(Java实现)
- 模拟视频光端机与数字光端机究竟有何区别
- 华为手机怎么强制关机_华为忘记锁屏密码怎么办?多品牌手机通用解锁密码
- Python猜字游戏(用函数)(最新版)
- CentOS 6.5安装使用docker
- 【工程项目经验】多个静态库打包成一个静态库
- java txt 写入pdf_java学习笔记 读取pdf文件并写成txt文件输出
- 开发者最讨厌的编程语言:Perl 名列第一,PHP、Objective-C 和 Ruby 都在其中
- 图灵机概念的javascript演示
- mysql查询id为5的数据类型_MySql学习05---数据类型
- 理解BERT每一层都学到了什么
- c#定时器Timer
- 从零开始打造自己的PHP框架——第3章
- Windows下搭建SVN服务器 - Visual SVN server
- 【备忘】老男孩IT教育_徐培成_大数据