首页 > 编程语言 >C程序函数调用&系统调用

C程序函数调用&系统调用

时间:2024-06-11 18:33:01浏览次数:31  
标签:调用 系统 程序 函数调用 指令 内核 执行 切换

理解程序的执行

我们要知道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)

标签:调用,系统,程序,函数调用,指令,内核,执行,切换
From: https://www.cnblogs.com/bymzy/p/18242551

相关文章

  • 淘宝扭蛋机小程序搭建:营销策略的精心布局
    随着移动互联网的快速发展,小程序成为了企业连接用户、提供服务的新桥梁。淘宝扭蛋机小程序作为一种创新的电商营销工具,通过独特的扭蛋机制,为用户带来趣味性和惊喜感。本文将探讨淘宝扭蛋机小程序搭建过程中的营销策略布局。一、明确目标用户群体在搭建淘宝扭蛋机小程序之前,首......
  • Linux系统通过CrossOver运行windows系统exe程序
    1.CrossOver下载下载网址:https://crossover.mairuan.com/?a_planid=462146305&a_unitid=9457631754&a_kw_enc_utf8=crossover+%E4%B8%8B%E8%BD%BD&a_creative=93751965498&cjtg=bdsem_cr_pp&bd_vid=11448931900093706309点击免费下载软件自动下载的安装包不是liunx需要的,需要下......
  • AWS无服务器 应用程序开发—第一章 目录
    越来越多的用户正在考虑采用无服务器架构进行云原生应用程序开发。通过引入无服务器架构,您将摆脱以前需要的服务器、操作系统和中间件的管理,并且能够显着降低成本。基于我们在应用程序开发和AWS构建方面的丰富知识,提供广泛的服务,开始介绍架构选择、设计、开发和维护。作......
  • 趣测小程序开发搭建
    一、趣测小程序简介小程序提供各种测试题,题目也可自定义上传,用户在小程序内找到喜欢的题目进行测试,测试完成后看广告得到答案,看广告得收益,对接流量主的小程序。二、趣测小程序功能搭建1.图文类小程序,流量主广告小程序.2.支持佣金提现.3.支持图文上传.4.支持参与小程序......
  • 构建LangChain应用程序的示例代码:27、FLARE:前瞻性主动检索增强生成技术实现与应用的示
    FLARE:前瞻性主动检索增强生成这个示例是前瞻性主动检索增强生成(FLARE)的实现。请查看原始仓库。基本思想是:开始回答问题如果开始生成模型不确定的标记,查找相关文档使用这些文档继续生成重复直到完成在查找相关文档的方式上有很多有趣的细节。基本上,模型不确定的标记会......
  • 微信小程序毕业设计-外卖点餐系统项目开发实战(附源码+演示视频+LW)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序支付、接单、取消订单和拒单都没在后端接收到参数,可能原因:
    原因是:支付要用到微信的接口weChatPayUtil进行pay操作接单、取消订单和拒单后,将进行退款,也要用到微信的接口weChatPayUtil退款//付款payJSONObjectjsonObject=weChatPayUtil.pay(ordersPaymentDTO.getOrderNumber(),//商户订单号newBigDecimal(0.01)......
  • 使用WPF 当程序已打开时第二次打开程序直接弹出第一次打开的程序
    在代码中增加[DllImport("user32.dll")]privatestaticexternboolSetForegroundWindow(IntPtrhWnd);[DllImport("user32.dll")]privatestaticexternboolShowWindowAsync(IntPtrhWnd,intnCmdShow);[DllImport("user32.dll"......
  • 使用使用rundll32 调用指定dll的方法
    使用使用rundll32调用指定dll的方法//顾名思义,"执行32位的DLL文件"。它的作用是执行DLL文件中的内部函数,这样在进程当中,只会有Rundll32.exe,而不会有DLL后门的进程,这样,就实现了进程上的隐藏。介绍一下Rundll32.exe这个文件,功能就是以命令行的方式调用动态链接程序库。系统中......
  • 基于springboot+vue.js+uniapp小程序的社区团购系统附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......