专栏首页每日分享java架构小知识写给Java程序员看的,CPU 上下文切换、用户态、内核态、进程与线程上下文切换(转)

写给Java程序员看的,CPU 上下文切换、用户态、内核态、进程与线程上下文切换(转)

1、概述

JDK源码中很多Native方法,特别是多线程、NIO部分,很多功能需要操作系统功能支持,作为Java程序员,如果要理解和掌握多线程和NIO等原理,就需要对操作系统的原理有所了解。

2、CPU 上下文切换

多任务操作系统中,多于CPU个数的任务同时运行就需要进行任务调度,从而多个任务轮流使用CPU。

从用户角度看好像所有的任务同时在运行,实际上是多个任务你运行一会,我运行一会,任务切换的速度很快,我们感觉不到而已。

而每个任务运行前,CPU需要知道从哪里加载这个任务的程序,还需要知道从程序哪行开始执行,这就要求OS事先帮任务设置好CPU的 寄存器程序计数器

CPU执行任务必须依赖的环境称为 CPU上下文

CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

CPU的上下文切换分为几种场景:进程上下文切换、线程上下文切换、中断上下文切换

2.1、用户态、内核态

Linux按特权等级,将进程的运行空间分为 内核空间用户空间

Intel x86架构使用了4个级别来标明不同的特权级权限。

R0实际就是内核态,拥有最高权限,可以直接访问所有资源(包括外围设备,例如硬盘,网卡等)。而一般应用程序处于R3状态–用户态

进程在用户空间运行时,被称为进程的 用户态,而陷入内核空间的时候,被称为进程的 内核态

R0最高可以读取R0-3所有的内容,R1可以读R1-3的,R2以此类推,R3只能读自己的数据。

2.2、为什么分内核态和用户态

假设没有这种内核态和用户态之分,程序随随便便就能访问硬件资源,比如说分配内存,程序能随意的读写所有的内存空间,如果程序员一不小心将不适当的内容写到了不该写的地方,就很可能导致系统崩溃。

用户程序进行系统调用后,操作系统执行一系列的检查验证,确保这次调用是安全的,再进行相应的资源访问操作。内核态能有效保护硬件资源的安全。

2.3、系统调用

从用户态到内核态的转变,需要通过系统调用来完成。比如,当我们查看文件内容时,就需要多次系统调用来完成:首先调用 open() 打开文件,然后调用 read() 读取文件内容,并调用 write() 将内容写到标准输出,最后再调用 close() 关闭文件。

系统调用会将CPU从用户态切换到核心态,以便 CPU 访问受到保护的内核内存。

系统调用的过程会发生 CPU 上下文的切换,CPU 寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。

而系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。

注意:系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。

系统调用过程通常称为特权模式切换,而不是进程上下文切换。

3、进程上下文切换

进程上下文切换跟系统调用又有什么区别呢?

首先,进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。

因此,进程的上下文切换就比系统调用时多了一步:在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、用户栈等保存下来;而加载下一个进程的内核态后,还需要加载这个进程的虚拟内存和用户栈。

根据 Tsuna 的测试报告,每次上下文切换都需要几十纳秒到数微秒的 CPU 时间,在进程上下文切换次数较多的情况下,这个时间对于CPU来说是相当可观的,会大大缩短CPU真正用于运行进程的时间。

3.1、什么时候会切换进程上下文?

只有在进程调度的时候,才需要切换上下文。Linux 为每个 CPU 都维护了一个就绪队列,将活跃进程(即正在运行和正在等待 CPU 的进程)按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。

新进程在什么时候才会被调度到 CPU 上运行呢? 1.运行中的进程执行完终止了,CPU 会释放出来,新的基础进程就可以被调度到CPU上运行了。 2. 运行中的进程时间片用完,进程被挂起 3. 运行中的进程资源不足,进程被挂起 4. 运行中的进程执行Sleep方法主动挂起 5. 新进程优先级更高,运行中的进程被挂起 6. 发生硬件中断,运行中的进程会被中断挂起,转而执行内核中的中断服务程序。

4、线程上下文切换

线程是调度的基本单位,而进程则是资源拥有的基本单位。

所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源。

当进程只有一个线程时,可以认为进程就等于线程,当进程拥有多个线程时,这些线程会共享进程的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。

线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。

线程的上下文切换其实就可以分为两种情况:

  1. 两个线程属于不同进程,因为资源不共享,切换过程和进程上线文切换一样
  2. 两个线程属于同一个进程,只需要切换线程的私有数据、寄存器等不共享的数据

5、总结

CPU上线文切换,切换寄存器、程序计数器

进程上线文切换,切换虚拟内存、用户栈

线程上下文切换,2种情况:(1)线程私有数据(比如线程栈、程序计数器等);(2)、(1)+ 线程资源 ;

系统调用:需要进行线程上下文切换,但不是进程上下文切换

R0实际就是内核态,拥有最高权限,可以直接访问所有资源(包括外围设备,例如硬盘,网卡等)。

应用程序处于R3状态–用户态

系统调用会进行 内核态用户态 转换

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MySQL架构与执行流程

      MySQL数据库自己用了也有两三年了,基本上只是掌握增删改查的sql语句,从没有思考过MySQL的内部到底是怎么根据sql查询数据的,包括索引的原理,只知道...

    会说话的丶猫
  • java设计模式-工厂方法模式

    工厂方法模式提供一个用于创建产品的接口,由实现类决定实现哪些产品。工厂方法模式使一个类的实例化延迟到子类,并且只适用于一个产品的等级结构。

    会说话的丶猫
  • 原子操作类

    原子性这个概念,在多线程编程里是一个老生常谈的问题。 所谓的原子性表示一个或者多个操作,要么全部执行完, 要么一个也不执行。不能出现成功一部分失败一部分的情 况...

    会说话的丶猫
  • 性能测试必备知识(5)- 深入理解“CPU 上下文切换”

    https://www.cnblogs.com/poloyy/category/1806772.html

    小菠萝测试笔记
  • 聊一聊CPU上下文切换

    多核CPU在运行任务时,使用程序计数器来存储当前执行指令的位置或者下一条将要执行的指令的位置,而CPU寄存器则是CPU中的内存。它们都是CPU在运行时必须依赖的...

    Java学习录
  • (41) 剖析HashSet / 计算机程序的思维逻辑

    查看历史文章,请点击上方链接关注公众号。 上节介绍了HashMap,提到了Set接口,Map接口的两个方法keySet和entrySet返回的都是Set,本节,...

    swiftma
  • 操作系统 进程线程模型 进程线程调度

    调度是分层次的,在操作系统中,一般将调度分为高级调度、中级调度和低级调度。 高级调度也称作业调度,其主要任务是按一定的原则,对磁盘中的处于后备状态的作业进行选择...

    Debug客栈
  • Python-GUI编程-PyQt5

    所以, GUI编程 不管是在实际开发工作中 还是在个人工具的开发中 都是必备之良品, 极为重要!

    py3study
  • C# 处理Word自动生成报告 四、程序处理

    FlyLolo
  • 自然语言处理中的预训练模型(下)

    PTM 通常是基于大规模文本语料训练通用的语言表示,而缺乏领域特定的知识。通过外部知识库引入领域知识被证明可以提升模型结果。这些外部知识包括:语言知识、语义知识...

    口仆

扫码关注云+社区

领取腾讯云代金券