理解程序的执行

我们要知道CPU可以自由地访问寄存器、内存。另外,程序是由操作系统执行的,所以操作系统能够控制程序的所有执行情况,限制程序的行为。

程序地执行过程:

  • 程序是一个二进制文件,包含程序的代码指令、代码中的文本信息等(参考C语言的程序的各种段)
  • 执行一个程序后,会将这个二进制加载到内存中,那么这个程序的代码(想象成各种汇编指令)也就记载道了内存中
  • CPU执行程序时从固定的位置main处开始执行(eip寄存器指向这里),逐条语句读取执行(这是CPU自带的功能)
    • 语句可能发生跳转(eip切换到其他汇编指令出)
    • 语句可能会操作栈(其实就是往一块特殊地内存空间写入数据、读出数据,CPU有相关的指令pop push解决这个问题)
  • 程序可能会执行系统调用(操作系统赋予的能力,例如读写文件,网络通信等)。现代操作系统将这些能力都放到了内核态来执行了,即只有内核代码才能做实际的读写文件操作,普通用户程序只能通过系统调用来执行这些能力。
    • 所以执行系统调用后,cpu就会相应地跳转到系统调用地入口处(这个系统调用的入口也时固定的,对应的是内核中的一段C代码
    • 内核的系统调用入口函数,根据系统调用号(对每个系统调用的标识),找到相应的处理函数执行(其实也是执行call函数)
    • 系统调用处理完后,继续返回到用户自己的程序代码处执行(所以,在执行系统调用前需要把用户代码执行的位置记录下来,并且在系统调用结束后自动设置eip指向这个地方)

函数调用

C语言函数调用关键

c语言函数调用的几个关键点在于:

  • 保护调用者的上下文(寄存器、栈指针(ebp,esp)信息)
  • 将传入参数通过esi、edi等放到被寄存器中、或者push到栈中(当参数比较多时)
  • 执行call调用函数,call的副作用是将eip压入到栈中
  • 将计算的返回值放到eax中
  • pop出ebp、esp
  • 执行ret,将eip从栈中pop出来,然后指令继续执行重新回到调用者上下文(将esp指向调用者调用函数后的语句)

系统调用

  • syscall sysenter sysret
  • int 0x80

在 x86-64 架构上,当应用程序需要执行系统调用时,CPU 会从用户态切换到内核态,经历以下过程:

  1. 用户态程序执行 syscall 指令:

    • 用户态程序通过执行 syscall 指令来触发系统调用请求。
  2. CPU 切换到内核态:
    • syscall 指令会引发一个特殊的异常,导致 CPU 从当前的用户态特权级切换到内核态的更高特权级。
    • 这个过程会自动保存用户态的部分寄存器状态,如 riprflags 等,并将控制权转交给内核。
  3. 内核处理系统调用:
    • 内核接管控制权后,会根据系统调用号找到对应的系统调用处理函数,并执行相应的操作。
    • 内核执行完成后,会将结果返回给用户态程序。
  4. 从内核态切换回用户态:
    • 内核执行完系统调用处理后,会通过 sysret 指令从内核态切换回用户态。
    • sysret 指令会自动恢复之前保存的用户态寄存器状态,并将控制权转回给用户态程序。

整个切换过程由硬件和操作系统内核共同完成,应用程序感知不到这个切换过程。这种基于硬件支持的用户态 - 内核态切换机制,能够大幅降低系统调用的开销,提高操作系统的整体性能。

需要注意的是,除了 syscall/sysret 指令,Intel 的 x86-64 架构也支持使用 int 0x80 软中断来执行系统调用,不过 syscall/sysret 方式通常更加高效。

linux系统调用 - chenjx_ucs - 博客园 (cnblogs.com)

cpu的特权级别(privilege level )

chatgpt告诉我们:Linux 系统通过以下几种方式来实现当前特权级别的切换:

  1. 系统调用:

    • 用户态应用程序通过系统调用机制从 Ring 3 切换到 Ring 0 内核态。
    • 应用程序执行 int 0x80 或 syscall 指令触发软中断,CPU 切换到 Ring 0 执行内核代码。
    • 内核处理完成后,通过 iret 或 sysret 指令返回到用户态。
  2. 中断/异常处理:
    • 当 CPU 遇到硬件中断或软件异常时, 会自动从当前特权级切换到 Ring 0 内核态。
    • 内核处理完中断/异常后,通过 iret 指令返回到之前的特权级别。
  3. 特权级切换指令:
    • x86 架构提供了一些用于特权级切换的指令,如 call gatetask gate 等。
    • 这些指令可以在不同特权级之间跳转,并自动完成上下文切换。
  4. 进程切换:
    • 当内核需要切换进程时,会切换进程的特权级别。
    • 内核将新进程的特权级别设置为 Ring 3,并通过 iret 指令返回到用户态。

在 Linux 中,大多数情况下都是通过系统调用和中断/异常处理来实现特权级切换。内核代码运行在 Ring 0 级别,用户态应用程序运行在 Ring 3 级别。当应用程序需要访问受保护的系统资源时,会通过系统调用陷入内核态,由内核代码执行相应的操作。中断和异常处理也会触发内核态的切换,内核负责处理各种硬件事件。总之,Linux 系统利用 CPU 硬件提供的特权级机制,通过系统调用、中断/异常处理、特权级切换指令等方式,实现了内核态和用户态之间的特权级切换,保证了系统的安全和稳定性。

Ring 0和Ring 3也有其他区别,例如Ring 0 程序可以执行所有的 CPU 指令集,包括特权指令。Ring 3 程序只能执行非特权指令集,无法直接执行特权级别的指令。

参考资料:

用户空间与内核空间,进程上下文与中断上下文[总结] - Rabbit_Dale - 博客园 (cnblogs.com)

C程序函数调用&系统调用的更多相关文章

  1. Linux Debugging(一): 使用反汇编理解C++程序函数调用栈

    拿到CoreDump后,如果看到的地址都是????,那么基本上可以确定,程序的栈被破坏掉了.GDB也是使用函数的调用栈去还原"事故现场"的.因此理解函数调用栈,是使用GDB进行现场 ...

  2. 微信小程序函数调用监控

    微信小程序之无埋点函数调用监控 有时候,面对一个bug,左思右想就是无法理解为什么. 我就有过这样的经历,耗时整个一个晚上,后来还是放弃了.不得不在所有可能的点都加上日志,部署等待再次报错,真的很让人 ...

  3. 源码分析:静态分析 C 程序函数调用关系图

    http://www.tinylab.org/callgraph-draw-the-calltree-of-c-functions/

  4. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  5. 《Linux内核设计与实现》读书笔记 第五章 系统调用

    第五章系统调用 系统调用是用户进程与内核进行交互的接口.为了保护系统稳定可靠,避免应用程序恣意忘形. 5.1与内核通信 系统调用在用户空间进程和硬件设备间添加了一个中间层, 作用:为用户空间提供了一种 ...

  6. Linux C 程序 进程控制(17)

    进程控制 1.进程概述现代操作系统的特点在于程序的并行执行.Linux是一个多用户多任务的操作系统.ps .pstree 查看进程进程除了进程id外还有一些其他标识信息,可以通过相应的函数获得.// ...

  7. Linux 系统调用过程详细分析

    内核版本:Linux-4.19 操作系统通过系统调用为运行于其上的进程提供服务. 那么,在应用程序内,调用一个系统调用的流程是怎样的呢? 我们以一个假设的系统调用 xyz() 为例,介绍一次系统调用的 ...

  8. linux内核剖析(六)Linux系统调用详解(实现机制分析)

    本文介绍了系统调用的一些实现细节.首先分析了系统调用的意义,它们与库函数和应用程序接口(API)有怎样的关系.然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递 ...

  9. Linux系统调用过程分析

    參考: <Linux内核设计与实现> 0 摘要 linux的系统调用过程: 层次例如以下: 用户程序------>C库(即API):INT 0x80 ----->system_ ...

  10. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

随机推荐

  1. 技术干货 | 阿里云数据库PostgreSQL 13大版本揭秘

    简介: 阿里云RDS PostgreSQL是一款兼容开源PostgreSQL的全托管云数据库产品,自2015年首次发布以来,根据用户需求不断升级迭代,已支持9.4.10.11.12等多个版本,覆盖了高 ...

  2. 【深度】阿里巴巴万级规模 K8s 集群全局高可用体系之美

    简介: 台湾作家林清玄在接受记者采访的时候,如此评价自己 30 多年写作生涯:"第一个十年我才华横溢,'贼光闪现',令周边黯然失色:第二个十年,我终于'宝光现形',不再去抢风头,反而与身边的 ...

  3. Snowflake如日中天是否代表Hadoop已死?大数据体系到底是什么?

    ​简介: 本文作者关涛是大数据系统领域的资深专家,在微软(互联网/Azure云事业群)和阿里巴巴(阿里云)经历了大数据发展20年过程中的后15年.本文试从系统架构的角度,就大数据架构热点,每条技术线的 ...

  4. dotnet OpenXML 解析 PPT 图表 解析日期时间表示内容

    在 OpenXML 里的图表存放的日期时间很多存放的是数值,需要进行转换才能获取到用户可见的字符串 日期在 OpenXML 里可以使用数值存放,需要根据数值和格式化字符串转换为用户可见的字符串 如以下 ...

  5. Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/xxx". at createRouterError 的说明和解决

    错误说明 Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: & ...

  6. Postergresql常见操作

    Postergresql常见操作 1. 安装部署 略 2. 登录数据库 查看版本 ## 以管理员身份 postgres 登陆,然后通过#psql -U postgres#sudo -i -u post ...

  7. python3生成随机单词或者语句

    pip install RandomWords Random words: >>> from random_words import RandomWords >>> ...

  8. .NET CORE 完美支持AOT 的 ORM SqlSugar 教程

    1.AOT适合产场 Aot适合工具类型的项目使用,优点禁止反编 ,第一次启动快,业务型项目或者反射多的项目不适合用AOT AOT更新记录: 实实在在经过实践的AOT ORM 5.1.4.117 +支持 ...

  9. SATA与PCI-E速度对比

    SATA SATA接口已经发展到了第三代,理论上的最大速度达到600MB/s.平时大家见到的SATA SSD使用的都是SATA三代,实际测试速度在550MB/s左右,这比普通的机械硬盘的速度100MB ...

  10. ansible系列(21)--ansible的变量注册Register

    1. 变量注册Register register 关键字可以将某个 task 任务结果存储至变量中,最后使用 debug模块 输出变量内容,可以用于后续排障: 示例一:register的基本使用: # ...