0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

系统调用:用户栈与内核栈的切换(下)

麦辣鸡腿堡 来源:技术简说 作者:董旭 2023-07-31 11:29 次阅读

接下来:call
do_syscall_64,进入do_syscall_64函数:

__visible void do_syscall_64(struct pt_regs *regs)
{
 struct thread_info *ti = current_thread_info();
 unsigned long nr = regs- >orig_ax;

 enter_from_user_mode();
 local_irq_enable();

 if (READ_ONCE(ti- >flags) & _TIF_WORK_SYSCALL_ENTRY)
  nr = syscall_trace_enter(regs);

 /*
  * NB: Native and x32 syscalls are dispatched from the same
  * table.  The only functional difference is the x32 bit in
  * regs- >orig_ax, which changes the behavior of some syscalls.
  */
 if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) {
  regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
   regs- >di, regs- >si, regs- >dx,
   regs- >r10, regs- >r8, regs- >r9);
 }

 syscall_return_slowpath(regs);
}

上述函数的主逻辑很简单:

1、 通过之前保存下来的pt_regs(往内核栈中格式化压入的),获取用户传入的系统调用号nr,系统调用号保存在了regs->orig_ax:

unsigned long nr = regs- >orig_ax;

2、 通过系统调用号nr,执行对应的回调函数,sys_call_table是函数指针数组,不同nr对应不同系统调用对应的函数。其中regx->di、regx->si、regs->dx、regs->r10、regs->r8、regs->r9分别是之前保存到内核栈(以struct
pt_regs格式化)保存到pt_regs中的,对应着用户传入该系统调用的参数1~6:

if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) {
  regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
   regs- >di, regs- >si, regs- >dx,
   regs- >r10, regs- >r8, regs- >r9);
 }

以上就完成了用户调用系统调用,并从用户栈切换到内核栈,并执行到系统调用号对应函数的过程。 具体的系统调用相关细节将在以后系统调用相关文章中分析。

系统调用-分析从内核栈切换用户栈

上面分析到了执行系统调用对应的函数,如下所示,并将返回值保存在regs->ax中了

regs- >ax = sys_call_table[nr & __SYSCALL_MASK](
   regs- >di, regs- >si, regs- >dx,
   regs- >r10, regs- >r8, regs- >r9);

函数执行到do_syscall_64->syscall_return_slowpath(regs),开始为返回用户态做准备.

并最终回到系统调用 内核SYSCALL 入口:ENTRY(entry_SYSCALL_64)->return_from_SYSCALL_64,继续完成系统调用返回工作,并切换用户栈与内核栈,使用struct pt_regs恢复用户态寄存器值。

总之

用户栈——>内核栈 cpu保存用户当前堆栈信息保存到内核的栈中(恢复时用到),然后将cpu指向内核堆栈,去执行内核代码。完成用用户栈到内核栈转换。

内核栈——>用户栈 再切换到内核堆栈前,将用户堆栈信息压入到内核栈中,内核函数执行完回退栈帧,会将用户的堆栈信息POP出栈,然后cpu堆栈寄存器就知道怎么回去了,返回的用户程序中断的地方继续执行。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内核
    +关注

    关注

    3

    文章

    1372

    浏览量

    40289
  • Linux
    +关注

    关注

    87

    文章

    11304

    浏览量

    209487
收藏 人收藏

    评论

    相关推荐

    从 Linux 内核的角度谈线程和进程

    1. 进程 进程是属于用户,和进程 虚拟地址空间(Virtual Address Space) 密切相关。那我们先了解下什么是虚拟地址空间:在32位机器
    的头像 发表于 09-25 15:23 2489次阅读
    从 Linux <b class='flag-5'>内核</b>的角度谈线程<b class='flag-5'>栈</b>和进程<b class='flag-5'>栈</b>

    C函数调用机制与帧原理详解

    当一个C函数被调用时,函数的参数如何传递、堆栈指针如何变化、帧是如何被建立以及如何被消除的,一直缺乏系统性的理解,因此决定花时间学习函数调用
    发表于 06-08 10:49 1336次阅读
    C函数<b class='flag-5'>调用</b>机制与<b class='flag-5'>栈</b>帧原理详解

    操作系统为什么分内核态和用户态?这两者如何切换

    操作系统为什么分内核态和用户态,这两者如何切换?进程在地址空间会划分为哪些区域?堆和有什么区别?
    发表于 07-23 09:01

    ARMv8的函数调用是什么意思?调用的内存管理是怎样的

    调用的函数称为非叶子函数,而无调用的则称为叶子函数Q2:正常情况是否需要感知函数调用
    发表于 05-13 10:36

    用一个实例展示一Linux内核帧的入和退过程

    1、Linux内核调试方法总结之帧  帧  帧和指针可以说是C语言的精髓。帧是一种特殊的数据结构,在C语言函数
    发表于 11-04 15:47

    一文详解Linux内核回溯与妙用

    网上或多或少都能找到回溯的一些文章,但是讲的都并不完整,没有将内核回溯的功能用于实际的内核、应用程序调试,这是本篇文章的核心:尽可能引导读者将
    的头像 发表于 10-05 10:02 5396次阅读
    一文详解Linux<b class='flag-5'>内核</b>的<b class='flag-5'>栈</b>回溯与妙用

    对Linux的进程内核的认识

    在每一个进程的生命周期中,必然会通过到系统调用陷入内核。在执行系统调用陷入内核之后,这些
    发表于 05-12 08:53 627次阅读
    对Linux的进程<b class='flag-5'>内核</b><b class='flag-5'>栈</b>的认识

    带你了解嵌入式C语言函数调用

    大家都知道函数调用是通过来实现的,而且知道在中存放着该函数的局部变量。但是对于的实现细节可能不一定清楚。
    发表于 07-12 17:08 2112次阅读
    带你了解嵌入式C语言函数<b class='flag-5'>调用</b><b class='flag-5'>栈</b>

    浅谈鸿蒙内核源码的CPU四次换,寄存器改值

    本篇有相当的难度,涉及用户内核的两轮切换,CPU四次换,寄存器改值,将围绕下图来说明. 
    的头像 发表于 04-28 16:56 1616次阅读
    浅谈鸿蒙<b class='flag-5'>内核</b>源码的CPU四次换<b class='flag-5'>栈</b>,寄存器改值

    浅谈鸿蒙内核源码的

    上面的代码和鸿蒙内核方式一样,都采用了递减满的方式, 什么是递减满?
    的头像 发表于 04-24 11:21 1442次阅读
    浅谈鸿蒙<b class='flag-5'>内核</b>源码的<b class='flag-5'>栈</b>

    鸿蒙内核源码分析:用户内核的两次切换

    这是系统调用的总入口,所有的系统调用都要跑这里要统一处理.通过系统号(保存在R7),找到注册函数并回调.完成
    的头像 发表于 04-23 17:17 1907次阅读
    鸿蒙<b class='flag-5'>内核</b>源码分析:<b class='flag-5'>用户</b><b class='flag-5'>栈</b>和<b class='flag-5'>内核</b><b class='flag-5'>栈</b>的两次<b class='flag-5'>切换</b>

    嵌入式系统的变化

    函数调用是通过来实现的,而且知道在中存放着该函数的局部变量。但是,对于的实现细节可能不一定清楚。本文将介绍一在Linux平台下函数
    的头像 发表于 12-29 16:40 1109次阅读
    嵌入式<b class='flag-5'>系统</b>中<b class='flag-5'>栈</b>的变化

    Linux中的进程、线程内核以及中断

    首先, (stack) 是一种串列形式的 数据结构。这种数据结构的特点是 后入先出 (LIFO, Last In First Out),数据只能在串列的一端 (称为:顶 top) 进行 推入
    的头像 发表于 05-14 09:30 700次阅读
    Linux中的进程<b class='flag-5'>栈</b>、线程<b class='flag-5'>栈</b>、<b class='flag-5'>内核</b><b class='flag-5'>栈</b>以及中断<b class='flag-5'>栈</b>

    系统调用用户内核切换(上)

    当发生系统调用、产生异常,外设发生中断等事件时,会发生用户内核之间的
    的头像 发表于 07-31 11:27 876次阅读
    <b class='flag-5'>系统</b><b class='flag-5'>调用</b>:<b class='flag-5'>用户</b><b class='flag-5'>栈</b>与<b class='flag-5'>内核</b><b class='flag-5'>栈</b>的<b class='flag-5'>切换</b>(上)

    linux中的进程,线程,内核的区别

    大多数的处理器架构,都有实现硬件。有专门的指针寄存器,以及特定的硬件指令来完成 入/出 的操作。例如在 ARM 架构上,R13 (SP) 指针是堆栈指针寄存器,而 PUSH 是
    发表于 08-18 10:57 518次阅读
    linux中的进程<b class='flag-5'>栈</b>,线程<b class='flag-5'>栈</b>,<b class='flag-5'>内核</b><b class='flag-5'>栈</b>的区别