为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”

21ic电子网 2026-07-29 16:10

什么是CPU上下文

Linux是一个多任务的操作系统,多任务操作系统是指多个进程运行在一个 CPU 中互不打扰,看起来像同时运行一样。多任务的操作系统这样就可以支持远大于CPU数量的任务"同时运行"。但是我们都知道这其实是一个错觉,真正是系统在很短的时间内将CPU轮流执行各个任务,给用户造成多任务"同时运行"的感觉。

其中有一个问题,在每次执行任务之前,CPU必须要知道从哪里加载任务,以及加载后从哪里开始运行,操作系统通过CPU中寄存器和程序计数器配合,来保存和恢复相应进度的信息

CPU 寄存器:CPU 寄存器是 CPU 内置的速度极快的内存; 程序计数器:程序计数器会存储 CPU 正在执行的指令位置,或者即将执行的指令位置。

在任务调度的过程中, 而这些信息都保存在CPU的寄存器中,其中即将执行的下一条指令的地址被保存在程序计数器上。我们将这些信息称为CPU的上下文,也叫硬件上下文

当某一进程自愿放弃它的 CPU 时间或者系统分配的时间片用完时,就会发生CPU上下文切换。

CPU上下文切换

操作系统OS在切换运行任务时,将上一任务的上下文保存起来,然后加载新任务的上下文到CPU寄存器,最后再跳转到程序计数器所指的新位置上执行新任务的这一动作,被称为CPU上下文切换

没有任何一项技术是凭空产生的。CPU 上下文及其切换机制的演进,本质上是一部“如何把昂贵的 CPU 算力压榨到极致”的苦难史。

为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图1

CPU上下文切换的步骤:

  1. 将前一个 CPU 的上下文(也就是 CPU 寄存器和程序计数器里边的内容)保存起来;
  2. 然后加载新任务的上下文到寄存器和程序计数器;
  3. 最后跳转到程序计数器所指的新位置,运行新任务。
  4. 被保存起来的上下文会存储到系统内核中,等待任务重新调度执行时再次加载进来。

CPU 的上下文切换分三种:进程上下文切换、线程上下文切换、中断上下文切换

上一任务的CPU上下文保存在哪?

我们知道因为CPU过于昂贵,其性能与其他储存设备有数量级的差距,为了充分压榨其性能,计算机将CPU的时间进行分片,让各个程序在CPU上轮转执行,被剥夺执行权的程序,等后面CPU继续执行它的时候,这时需要一个数据结构来保存相关信息,以便之后恢复继续执行,这个其实就是进程。

CPU上下文会被保存在进程的内核空间(kernel space)上。OS在给每个进程分配虚拟内存空间时,会分配一个内核空间,这部分内存只能由内核代码访问。

OS在切换CPU上下文前,会先将当前CPU的通用寄存器、PC等进程现场信息保存在进程的内核空间上,待下次切换时,再取出重新装载到CPU上,以恢复任务的运行。为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图2

进程上下文切换

内核空间和用户空间

我们知道为了限制不同的指令的访问能力,提升安全,Linux 按照特权等级,把进程的运行空间分为内核空间用户空间 。进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态

  1. 内核空间(Ring 0):具有最高权限,可以直接访问所有资源(读取文件)

常见的内核操作:分配内存、IO操作、创建子进程……

  1. 用户空间(Ring 3):只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用进入到内核中,才能访问这些特权资源

常见的用户态空间程序:数据库、web服务器、shell脚本、Java程序或者其他常见语言的程序……

我们一起看下Linux整体架构图:为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图3

top命令查看CPU资源

在linux系统使用top命令查看cpu时,能看到用户态和内核态占用的cpu资源为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图4其中各项数据表示内容:

us
用户空间占用CPU百分比
sy
内核空间占用CPU百分比
ni
用户进程空间内改变过优先级的进程占用CPU百分比
id
空闲CPU百分比
wa
等待输入输出的CPU时间百分比
hi
硬件中断
si
软件中断
st
实时

系统调用

对于一个进程来说,比如web服务的进程,一般是运行在用户态的,但是当需要访问内存、磁盘等硬件设备的时候需要先进入到内核态中,也就是从用户态到内核态的转变,而这种转变需要借助系统调用来实现。系统调用是内核向用户进程提供服务的唯一方法。

比如查看文件时,需要执行多次系统调用:open()打开文件,read()读取文件内容,write()将文件内容输出到控制台,最后close()关闭文件等。

系统调用的过程如下:

  1. 把 CPU 寄存器里原来用户态的指令位置保存起来;
  2. 为了执行内核代码,CPU 寄存器需要更新为内核态指令的新位置,最后跳转到内核态运行内核任务;
  3. 系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程;

我们可以发现一次系统调用的过程,其实是发生了两次 CPU 上下文切换(用户态-内核态-用户态)。

需要注意的是:系统调用过程中,不涉及虚拟内存等进程用户态的资源,也不会切换进程,也就是系统调用过程中一直是同一个进程在运行。系统调用过程也通常称为特权模式切换

进程上下文切换 和 系统调用的区别?

  1. 进程上下文切换是指,从一个进程切换到另一个进程;系统调用过程一直是同一个进程在运行,属于进程之内的上下文切换

需要注意的是:进程是由内核来管理和调度的,进程的切换只能发生在内核态,保存上下文和恢复上下文的过程并不免费,需要消耗一定资源

为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图5
  1. 进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。而系统调用这里没有涉及到虚拟内存等这些进程用户态的资源

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

进程切换的常见场景

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

进程切换的场景有:

  1. 进程时间片耗尽,为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行。
  2. 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行。
  3. 进程通过睡眠函数 sleep 主动把自己挂起,CPU会重新调度;
  4. 当有CPU发现优先级更高的进程运行时,为了去运行高优先级进程,当前进程会被挂起;
  5. 发生硬中断,CPU 上的进程会被挂起,然后去执行内核中的中断服务进程。

线程上下文切换

对操作系统来说,进程是资源分配的基本单位,而线程则是任务调度的基本单位。内核中的任务调度实际是在调度线程,进程只是给线程提供虚拟内存、全局变量等资源。

线程上下文切换时,共享相同的虚拟内存和全局变量等资源不需要修改。而线程自己的私有数据,如栈和寄存器等,上下文切换时需要保存。

关于进程线程的区别:

  • 当进程中只有一个线程时,可以认为进程就等于线程。

  • 当进程拥有多个线程时,这些线程会共享父进程的资源(即共享相同的虚拟内存和全局变量等资源)。这些资源在上下文切换时是不需要修改的。

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

    为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图6

因此线程上下文切换有两种情况:

  • 前后两个线程属于不同进程,因为资源不共享,所以切换过程就跟进程上下文切换是一样的;
  • 前后两个线程属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。

中断上下文切换

上下文切换有时也因硬件中断而触发。硬件中断是指硬件设备(如键盘、鼠标、调试解调器、系统时钟)给内核发送的一个信号,该信号表示一个事件(如按键、鼠标移动、从网络连接接收到数据)发生了。

为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图7

为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,然后调用中断处理程序,响应设备事件。在打断其他进程时,需要先将进程当前的状态保存下来,等中断结束后,进程仍然可以恢复回来。

跟进程上下文不同,中断上下文切换不涉及进程的用户态。所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。中断上下文,只包括内核态中断服务程序执行所必需的状态,也就是 CPU 寄存器、内核堆栈、硬件中断参数等。

中断上下文切换并不涉及到进程的用户态。所以即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。中断上下文,其实只包括内核态中断服务程序执行所必须的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。

对同一个 CPU 来说,中断处理比进程拥有更高的优先级,所以中断上下文切换不会与进程上下文切换同时发生。并且,由于中断会打断正常进程的调度和执行,所以大部分中断处理程序都短小精悍,以便可以尽快完成。

上下文切换的消耗

保存上下文恢复上下文的过程并不是免费的,需要内核在 CPU 上运行才能完成。据测试,每次上下文切换都需要几十纳秒到数微妙的 CPU 时间。特别是在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间消耗在寄存器、内核栈、虚拟内存等资源的保存和恢复上,从而大大缩短了真正运行进程的时间。

Linux相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少

Linux通过TLB 来管理虚拟内存到物理内存的映射关系`。当虚拟内存更新后,TLB 也需要刷新,内存的访问也会随之变慢。特别是多处理器系统上,缓存是被多个处理器共享的,刷新缓存不仅会影响当前处理器的进程,还会影响共享缓存的其它处理器的进程。所以过多的上下文切换对系统来说意味着会消耗大量的 CPU 时间。

根据Tsuna的测试报告,每次上下文切换都需要几十纳秒到数微妙的CPU时间,这个时间还是相当可观的。 不管是哪种场景导致的上下文切换,你都应该知道:

  1. CPU上下文切换,是保证Linux系统正常工作的核心功能,一般情况下不需要开发人员特别关注。
  2. 但过多的上下文切换,会把CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,耗费大量的 CPU,甚至严重降低系统的整体性能。

END


为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图8


最后提一句,21ic论坛(bbs.21ic.com)正在招募原创作者,单篇文章奖励最高500元,欢迎广大网友踊跃投稿!为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图9 

往期精选:


为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图10
扫描二维码,关注视频号

请点下【♡】给小编加鸡腿



为什么你的 CPU 总是跑不爽?聊聊被 90% 程序员忽略的“上下文切换”图11

关于科技区角:国内科技展会垂直内容策划服务商,提供从论坛内容全案策划、会展市场化IP打造到精准专业观众一站式邀约服务,以产业内容吸引高质量B端人群,打通展会从议题设计、演讲嘉宾邀约、宣传预热、精准邀观到供需对接全链路。
声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。
more
翻车了!添可无线吸尘器拆解维修,遇见从未见过的芯片封装!
聚焦光储高压电力电子系统,德氪微在 SNEC 2026 展示毫米波无线隔离方案
6G 射频建模与仿真学习指南:数字孪生引领下一代无线设计
领科网络:为全球客户打造稳定可靠无线连接,8月IOTE国际物联网展10C62与您相约
8.5亿全资收购!剑指国产无线物联网芯片龙头
无线图传和宽带自组网有什么区别?不要把宽带自组网想得太复杂!
拆解报告:inno3C 20W磁吸无线充移动电源
智融科技单电芯30W超薄Qi2.2新国标移动电源无线充方案解析
资讯速递|Paradromics无线脑机接口完成首次正式人体植入手术
易冲推出15W高集成无线发射芯片WB8118
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号