用户代码和操作系统代码是如何在CPU上面运行的(用户态和内核态)

简介: 用户代码和操作系统代码是如何在CPU上面运行的(用户态和内核态)

1.CPU的四种状态与操作系统的两种状态(用户态和内核态)

  • 首先我们要知道CPU有四种状态,分别为编号为0(特权最大)到3(特权最小),以及3个受保护的主要资源:内存、I/O端口和执行某些机器指令的能力。
  • 操作系统它基于CPU之上,只用到了CPU的两种状态,一个内核态,一个用户态,内核态运行在CPU的第 0 等级,用户态运行在CPU的第 3 等级。

在这里插入图片描述

2.操作系统的用户态和内核态之间的切换

  1. 首先内核态与用户态是操作系统的两种运行级别,跟intel cpu没有必然的联系, intel cpu提供Ring0-Ring3三种级别的运行模式,Ring0级别最高,Ring3最低。
  2. 其次Linux使用了Ring3级别运行用户态,Ring0作为 内核态,没有使用Ring1和Ring2。Ring3状态不能访问Ring0的地址空间这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据)。
  3. 然后用户运行一个程序,该程序所创建的进程开始是运行在用户态的,如果要执行文件操作,网络数据发送等操作,必须通过write,send等系统调用(比如netty和redis中对于多路复用select和poll的改进epoll也需要切换到内核态),这些系统调用会调用内核中的代码来完成操作,这时,必须切换到Ring0,然后进入内核地址空间去执行这些代码完成操作,完成后,切换回Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。
  4. 最后至于说保护模式,是说通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程的地址空间中的数据。

3.操作系统的用户态切换到内核态的四种情况

  • 系统调用
    这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如x86里的call gate也可以用来做系统调用,也能做到权限控制和内核代码保护,跟中断的效果完全一样,

  • 硬中断

    • 当外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。该中断可被屏蔽。
    • 从本质上讲,中断(硬)是一种电信号,当设备有某种事情发生的时候,他就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的(与逻辑门的另外一边设置的是高电平,如果是低电平就进行了屏蔽),中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。
  • 软中断(softIRQ)

    • 软中断(softIRQ)的一种典型应用就是所谓的"下半部"(bottom half),它的得名来自于将硬件中断处理分离成"上半部"和"下半部"两个阶段的机制:上半部在屏蔽中断的上下文中运行,用于完成关键性的处理动作;而下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。
    • 在系统调用的时候,也可以用软中断的方式实现,比如 fork() 实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如 Linux 的 int 80h 中断。该中断不可被屏蔽。
  • 异常
    当 CPU 在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

    • 首先明确下什么是缺页异常,CPU通过地址总线可以访问连接在地址总线上的所有外设,包括物理内存、IO设备等等,但从CPU发出的访问地址并非是这些外设在地址总线上的物理地址,而是一个虚拟地址,由MMU将虚拟地址转换成物理地址再从地址总线上发出,MMU上的这种虚拟地址和物理地址的转换关系是需要创建的,并且MMU还可以设置这个物理页是否可以进行写操作,当没有创建一个虚拟地址到物理地址的映射,或者创建了这样的映射,但那个物理页不可写的时候,MMU将会通知CPU产生了一个缺页异常。
    • 下面总结下缺页异常的几种情况:
      1、当MMU中确实没有创建虚拟页物理页映射关系,并且在该虚拟地址之后再没有当前进程的线性区vma的时候,可以肯定这是一个编码错误,这将杀掉该进程;
      2、当MMU中确实没有创建虚拟页物理页映射关系,并且在该虚拟地址之后存在当前进程的线性区vma的时候,这很可能是缺页异常,并且可能是栈溢出导致的缺页异常;
      3、当使用malloc/mmap等希望访问物理空间的库函数/系统调用后,由于linux并未真正给新创建的vma映射物理页,此时若先进行写操作,将如上面的2的情况产生缺页异常,若先进行读操作虽也会产生缺页异常,将被映射给默认的零页(zero_pfn),等再进行写操作时,仍会产生缺页异常,这次必须分配物理页了,进入写时复制的流程;
      4、当使用fork等系统调用创建子进程时,子进程不论有无自己的vma,“它的”vma都有对于物理页的映射,但它们共同映射的这些物理页属性为只读,即linux并未给子进程真正分配物理页,当父子进程任何一方要写相应物理页时,导致缺页异常的写时复制。

4.用户代码和操作系统代码是如何在cup上面运行的

4.1操作系统运行用户程序?

  • 计算机的代码,都是机器码,都由CPU一条一条地执行的,无论是应用程序的代码,还是操作系统的代码,都是机器码,CPU都用一样的方法来执行,这个问题和内核态还是用户态无关。所以,没有“操作系统运行用户程序”的说法。所有的软件指令,都被CPU用一样的方法执行。

4.2为什么同是代码,操作系统程序会比一般程序拥有更高的权限?

  • 权限是CPU制造的,CPU只要能保证权限的穿越是单向的,就可以赋予操作系统(的代码)特殊的权限。比如ARM64 CPU启动的时候,(在特定的设计下),工作在权限EL1,这时开始执行的代码就是操作系统的代码了,操作系统执行够了以后,主动把CPU权限降低到EL0(EL1的时候你有权降级,但反过来你就没有权利),这之后执行的代码就在所谓的“用户态"了,由于CPU工作在EL0状态,这些代码的权限就很低,这时如果你执行一个权限比较高的指令(比如访问SCTLR_EL1.A寄存器,又比如x86的int,ARM的SC),CPU就会报错(报错的结果是把权限切换回EL1,并且直接调用操作系统设置好的代码,这样控制权仍是操作系统的),把用户程序的控制权强行取走,赋予给操作系统,这就是为什么操作系统(表现出来)比用户程序拥有更高的权限。
  • 所谓操作系统,用户程序,系统服务,都是我们基于CPU权限人为制造的概念,并非必须存在的客观实体。

4.3代码是如何成为操作系统的?

  • CPU开始加电了,完成内部必要的处理后,就可以从指定的内存地址开始执行代码,这个内存地址称为Reset向量,在一些CPU上是固定的,在一些CPU上是可以根据特定的条件变化的(比如把CPU连入电路的时候,给某个引脚加高电平等),但无论如何,反正最终CPU会在某个固定的位置开始执行程序。什么代码放在这里,什么代码就具有最高的执行权限。
  • 在DOS+x86的时代,硬件设计者会在这个内存位置上固定焊一个ROM,然后在计算机出厂的时候,固定在这个ROM上烧一段程序,这个程序就称为BIOS,所以,CPU加电后,首先进入的是BIOS程序,然后有BIOS程序根据你是否有磁盘,从磁盘的指定位置上读入代码来执行。
  • 所以你要成为操作系统,你就要把你的代码放到磁盘的指定位置,这样这个代码就会具备“操作系统”的控制权。

4.4现代的计算机CPU是如何加载操作系统代码的?

  • 现代更复杂的计算机,硬件加电的时候,根本还没有给主CPU加电,而是某个MCU(小CPU)首先获得系统的控制权,这个MCU就可以直接设置固定的内存内的内容,让CPU到时执行这段代码,这样,其实真正具有系统的把控权的是这个MCU,它的代码才是系统的“固件”。
  • 如果是多核CPU,CPU加电的时候仅启动一个核,完成前期的大部分初始化(比如安全操作系统和虚拟机调度器的初始化),然后进入EL1,完成主CPU的初始化,在这个主CPU初始化的时候,准备其他CPU的Reset向量的内容,完成准备后,控制硬件给其他CPU加电,这样其他CPU也在操作系统的控制下投入运行。整个系统就都在控制之下了。
            </div>
目录
相关文章
|
3月前
|
C++
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
43 0
|
2月前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
2月前
|
存储 算法 安全
深入理解操作系统:从基础概念到代码实践
【9月更文挑战第23天】本文将带领读者深入探索操作系统的奥秘,从基础概念出发,逐步揭示操作系统的工作原理和设计哲学。我们将通过实际代码示例,展示操作系统如何与硬件交互、管理资源以及提供用户界面。无论你是计算机专业的学生还是对操作系统感兴趣的开发者,这篇文章都将为你打开一扇通往操作系统世界的大门。
67 16
|
1月前
|
小程序 iOS开发 MacOS
MacOS环境-手写操作系统-44-运行简单的程序
MacOS环境-手写操作系统-44-运行简单的程序
19 0
|
2月前
|
存储 算法 Unix
探索操作系统:从理论到代码
【9月更文挑战第3天】操作系统是计算机的核心,它管理着硬件和软件之间的交互。本文将从理论出发,深入探讨操作系统的基本原理和功能,然后通过代码示例,展示操作系统是如何在实际中运作的。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深度理解。
|
2月前
|
调度
CPU调度器实现提示:针对特定体系结构代码【ChatGPT】
CPU调度器实现提示:针对特定体系结构代码【ChatGPT】
|
3月前
|
设计模式 uml
在电脑主机(MainFrame)中只需要按下主机的开机按钮(on()),即可调用其它硬件设备和软件的启动方法,如内存(Memory)的自检(check())、CPU的运行(run())、硬盘(Hard
该博客文章通过一个电脑主机启动的示例代码,展示了外观模式(Facade Pattern)的设计模式,其中主机(MainFrame)类通过调用内部硬件组件(如内存、CPU、硬盘)和操作系统的启动方法来实现开机流程,同时讨论了外观模式的优缺点。
|
3月前
|
数据采集 数据可视化 数据挖掘
使用Python进行数据分析的新手指南深入浅出操作系统:从理论到代码实践
【8月更文挑战第30天】在数据驱动的世界中,掌握数据分析技能变得越来越重要。本文将引导你通过Python这门强大的编程语言来探索数据分析的世界。我们将从安装必要的软件包开始,逐步学习如何导入和清洗数据,以及如何使用Pandas库进行数据操作。文章最后会介绍如何使用Matplotlib和Seaborn库来绘制数据图表,帮助你以视觉方式理解数据。无论你是编程新手还是有经验的开发者,这篇文章都将为你打开数据分析的大门。
|
3月前
|
测试技术 数据安全/隐私保护 Python
探索Python中的装饰器:简化代码,增强功能深入理解操作系统:从用户空间到内核空间的旅程
【8月更文挑战第29天】本文将引导你深入理解Python装饰器的核心概念、应用场景及其对代码的优化作用。我们将从基础使用到高级应用逐步展开,通过实例展示如何利用装饰器提升代码的可读性和复用性,同时避免常见的陷阱。
|
4月前
|
负载均衡 算法 应用服务中间件
nginx自定义负载均衡及根据cpu运行自定义负载均衡
nginx自定义负载均衡及根据cpu运行自定义负载均衡
52 1