性能测试必备知识(5)- 深入理解“CPU 上下文切换”
做性能测试的必备知识系列,可以看下面链接的文章哦
https://www.cnblogs.com/poloyy/category/1806772.html
前言
上一篇文章中,举例了大量进程等待 CPU 调度的场景
灵魂拷问
既然进程是在等待,并没有运行,为什么系统的平均负载还是会升高呢
回答
本文的重点:CPU 上下文切换就是罪魁祸首
先来聊聊 Linux
提出疑问
- 之前说最好一个 CPU 运行一个进程,这样 CPU 利用率刚刚好
- 但事实上我们的 Linux 会同时运行很多进程,包括系统态的和自己启动的进程,这不就违背了我们的美好初衷吗?
知识点来回答疑问
- Linux 是一个多任务操作系统
- 它支持远大于 CPU 数量的任务同时运行
- 但多任务其实并不是真的在同时运行
- 而是因为系统在很短时间内,将 CPU 轮流分配给它们,造成多任务同时运行的错觉
什么是 CPU 上下文
CPU 寄存器和程序计数器(PC)
- 在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行
- 所以需要系统事先帮它设置好 CPU 寄存器和程序计数器
CPU 寄存器
CPU 内置的容量小,但速度极快的内存
程序计数器
用来存储 CPU 正在执行的指令位置,或者即将执行的下一条指令位置
CPU 上下文
CPU 寄存器和程序计数器是 CPU 在运行任何任务前,必须的依赖环境,所以也被叫做 CPU 上下文
CPU 上下文切换
分步骤去理解
- 先把前一个任务的 CPU 上下文(CPU 寄存器和程序计数器)保存起来
- 加载新任务的上下文到这些寄存器和程序计数器
- 最后再跳转到程序计数器所指的新位置,运行新任务
- 保存下来的上下文,会存储到系统内核中,并在任务重新调度执行时再次加载进来,这样能保证任务原来的状态不受影响,让任务看起来还是连续运行
灵魂拷问一
CPU 上下文切换无非就是更新了 CPU 寄存器的值嘛,但这些寄存器,本身就是为了快速运行任务而设计的,为什么会影响系统的 CPU 性能呢?
灵魂拷问二
- 上面老说到的【任务】到底是什么呢?
- 是进程,线程?是的,进程和线程是最常见的任务
- 那除此之外,还有其他的任务吗?
回答
硬件通过触发信号,会导致中断处理程序的调用,也是一种常见的任务
所以,根据任务的不同,CPU 的上下文切换可以分为不同的场景
- 进程上下文切换
- 线程上下文切换
- 中断上下文切换
系统调用
Linux 按照特权等级划分进程的运行空间
分为
- 内核空间(Ring 0):具有最高权限,可以直接访问所有资源
- 用户空间(Ring 3):只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些特权资源
也就是说,进程既可以在用户空间运行,称为进程的用户态
又可以在内核空间运行,称为进程的内核态
重点:用户态到内核态的转变需要通过系统调用来完成
系统调用的栗子
比如,当我们查看文件内容时,就需要多次系统调用来完成:
- 首先调用 open() 打开文件
- 然后调用 read() 读取文件内容
- 并调用 write() 将内容输出
- 最后调用 close() 关闭文件
系统调用的过程发生 CPU 上下文的切换
- CPU 寄存器里原来用户态的指令位置,需要先保存起来
- 为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置
- 最后才是跳转到内核态运行内核任务
- 系统调用结束后,CPU 寄存器需要恢复原来保存的用户态
- 然后再切换回用户空间,继续运行进程
总结下
- 一次系统调用的过程,其实发生了两次 CPU 上下文切换(用户态切内核态,内核态再切回用户态)
- 系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程
和进程上下文切换的不同
- 进程上下文切换:从一个进程切换到另一个进程运行
- 系统调用:一直是同一个进程在运行
总结
- 系统调用过程通常称为特权模式切换,而不是上下文切换
- 但实际上,系统调用过程中, CPU 上下文切换是无法避免的
进程上下文切换
基础知识点
- 在 Linux 中,进程是由内核来管理和调度
- 进程的切换只能发生在内核态
- 所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态
进程的上下文切换就比系统调用时多了一步
- 在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、栈等保存下来【保存上下文】
- 而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈【加载上下文】
保存上下文和加载上下文的过程需要内核在 CPU 上运行才能完成
进程上下文切换如何影响系统性能?
- 根据 Tsuna的测试报告,每次上下文切换都需要几十纳秒到数微秒的 CPU 时间
- 这个时间还是略大的,特别是在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间
- 这也正是上一篇文章中讲到的,导致平均负载升高的一个重要因素
TLB 也会受影响?
- TLB(Transaction Lookaside Buffer)来管理虚拟内存到物理内存的映射关系
- 当虚拟内存更新后,TLB 也需要刷新,内存的访问也会变慢
- 特别是在多处理器系统上,缓存是被多个处理器共享的,刷新缓存不仅会影响当前处理器的进程,还会影响共享缓存的其他处理器的进程
什么时候会切换进程上下文
- 顾名思义,只有在进程切换时才需要切换上下文
- 换句话说,只有在进程调度时才需要切换上下文
CPU 如何挑选进程来运行?
- Linux 为每个 CPU 都维护了一个等待队列
- 将活跃进程(正在运行和正在等待 CPU 的进程)按照优先级和等待 CPU 的时间排序
- 然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行
进程什么时候才会被调度到 CPU 上运行?
1 - 主动释放
进程执行完终止了,会释放 CPU,这时候从等待队列中拿一个新的进程来运行
2 - 时间片轮转
为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程,当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行
3 - 资源不足
进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候 进程也会被挂起,并由系统调度其他进程运行
4 - sleep 函数
当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度
5 - 优先级高
当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行
6 - 硬中断
发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序
线程上下文切换
先来聊下线程和进程的关系
- 线程和进程的最大区别在于:线程是调度的基本单位,进程是资源分配的基本单位
- 内核中的任务调度,实际上的调度对象是线程
- 而进程只是给线程提供了虚拟内存、全局变量等资源
- 当进程只有一个线程时,可以任务进程=线程
- 当进程有多个线程时,线程会共享进程的虚拟内存和全局变量等资源,在线程上下文切换时这些资源是不需要修改的
- 线程也有独立的数据,比如栈、寄存器等,这些在线程上下文切换时是需要保存的
线程上下文切换的场景一
- 前后两个线程属于不同进程
- 此时,因为不同进程的资源不共享,所以线程上下文切换 等同于 进程上下文切换
线程上下文切换的场景二
- 前后两个线程属于同一个进程
- 此时,因为同一进程的资源是共享的,所以在切换时,虚拟内存这些资源就保持不动
- 只需要切换线程的私有数据、寄存器等不共享的数据
多线程的优势
线程上下文切换对比进程上下文切换,很明显切换消耗的资源会更少,所以多线程比多进程更有优势
中断上下文切换
中断处理
为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件
在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行
和进程上下文切换的不同点
- 中断上下文切换并不涉及到进程的用户态
- 即便中断过程打断了 一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源
- 中断上下文,只包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数
中断上下文不会和进程上下文切换同时发生
- 对同一个 CPU 来说,中断处理比进程拥有更高的优先级
- 由于中断会打断正常进程的调度和执行,所以大部分中断处理程序都短小精悍,以便尽可能快的执行结束
耗资源程度
- 跟进程上下文切换一样,中断上下文切换也需要消耗 CPU,切换次数过多也会耗费大量的 CPU,甚至严重降低系统的整体性能
- 当发现中断次数过多时,就需要注意去排查它是否会给你的系统带来严重的性能问题
CPU 上下文切换的总结
- CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要关注【CPU 上下文切换是正常核心功能值】
- 但过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈、虚拟内存等数据的保存和恢复上,从而缩短进程真正的运行时间,导致系统的整体性能大幅下降【数据保存和恢复时间增加,进程运行时间减少,性能下降】
性能测试必备知识(5)- 深入理解“CPU 上下文切换”的更多相关文章
- 如何理解CPU上下文切换(二)
如何理解CPU上下文切换(二) 1.引 你们好,可爱的小伙伴们.^_^ 多个进程竞争CPU就是一个经常被我们忽视的问题. 你们一定很好奇,进程在竞争CPU的时候并没有真正运行,为什么还会导致系统的负载 ...
- 性能测试必备知识(6)- 如何查看“CPU 上下文切换”
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 课前准备,安装 sysbench 下载 sy ...
- 性能测试必备知识(7)- 深入理解“CPU 使用率”
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 回顾 CPU 使用率是单位时间内 CPU 使 ...
- 性能测试必备知识(2)- 查看 Linux 的 CPU 相关信息
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 查看系统 CPU 信息 cat /proc/ ...
- 性能测试必备知识(11)- 怎么理解内存中的Buffer和Cache?
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 缓存 从 free 命令可以看到,缓存其实就 ...
- 性能测试必备知识(4)- 使用 stress 和 sysstat
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html stress 介绍 Linux 系统压力测试 ...
- 性能测试必备知识(10)- Linux 是怎么管理内存的?
做性能测试的必备知识系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1806772.html 内存映射 日常生活常说的内存是什么 比方说, ...
- 从Java视角理解CPU上下文切换(Context Switch)
从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态 在高性能编程时,经常接触到多线程. 起初我们的理解是, 多个线程并行地执行总比单个线程要快, 就像多个人一起干活总比一个人干要快 ...
- 03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)
上一节,我给你讲了要怎么理解平均负载( Load Average),并用三个案例展示了不同场景下平均负载升高的分析方法.这其中,多个进程竞争 CPU 就是一个经常被我们忽视的问题. 我想你一定很好奇, ...
随机推荐
- vue-cli按装 和vue创建项目
安装 创建
- 字符串String和list集合判空验证
1`字符串判断处理: 结论: 当if判断条件为两个,并且它们两个为或的关系,如果第一个条件为false,则继续第二个条件的判断:如果第一个条件为true,该例子不足以说明是否判断第二个条件, 最终可以 ...
- LeetCode62. 不同路径
由于机器人只可以向右和向下移动,所以我们要到第i行第j列,只可以由第i-1行第j列和第i行第j-1列移动一步得到,因此要到第i行第j列的方案数就是到第i-1行第j列的方案数和到第i行第j-1列的方案数 ...
- 半导体质量管理_eCAP LineWorks SPACE eCAP(电子OCAP)
用于设计和实施纠正措施的自动化工作流程 以电子方式管理纠正措施的工作流程 LineWorks SPACE的此附加程序提供了设计和自动实施纠正措施(电子纠正措施计划,eCAP)的工作流程.这样可以确保您 ...
- NXP S32V eiq_auto tensorflow offline tool 环境配置
NXP S32V eiq_auto tensorflow offline tool 环境配置 完成cnn模型eiq移植的第一步 1.安装conda 下载.sh bash Anaconda3-5.3.1 ...
- Python-自动用0补取长度
描述 Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0. 语法 zfill()方法语法: str.zfill(width) 参数 width -- 指定字符串的长度. ...
- 关于jwt6.0.0版本algorithms should be set报错的解决方案
2020.7.7日jwt更新之后,安装的express-jwt模块会默认为6.0.0版本,我将之前的auth.js文件引入时控制台报错,提示algorithms should be set,中文译为应 ...
- java学习第四天7/9
一. 今天学习了一些算法: 求最小值,最大值,平均值 接着学了几种排序方法 1.冒泡排序 (1)逐一比较数组中响铃的两个元素,如果后面的数字小于前面的数字,就交换先后元素: (2)经过一个轮次的比较, ...
- CSS(二)- 选择器 - 伪元素和伪类(思维导图)
伪元素 伪元素可以创建一些文档语言无法创建的虚拟元素.比如:文档语言没有一种机制可以描述元素内容的第一个字母或第一行,但伪元素可以做到(::first-letter.::first-line).同时, ...
- Scala 基础(十一):Scala 函数式编程(三)高级(一)偏函数、作为参数的函数、匿名函数、高阶函数
1 偏函数 1)在对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择 2)将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值的 ...