性能测试必备知识(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 就是一个经常被我们忽视的问题. 我想你一定很好奇, ...
随机推荐
- TestNG学习笔记新的
1.参考官方文档https://www.yiibai.com/html/testng/2013/0915300.html package com.cib.testng; import org.test ...
- java程序员软件测试技巧
测试是开发的一个非常重要的方面,可以在很大程度上决定一个应用程序的命运.良好的测试可以在早期捕获导致应用程序崩溃的问题,但较差的测试往往总是导致故障和停机. 测试是开发的一个非常重要的方面,可以在很大 ...
- 03.基于测试开发讲解和Cobertura框架介绍
首先我们先 CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(200) DEFAULT ...
- Halcon斑点分析BlobAnalysis解析
斑点分析的算法非常简单:在图像中,相关对象的像素(也称为前景)通过其灰度值来识别.例如,图中示例显示了液体中的组织颗粒.这些粒子是明亮的,液体(背景)是暗的.通过选择明亮的像素(阈值),可以很容易检测 ...
- ajax前后端交互原理(5)
5.ajax简介 5.1.什么是ajax Asynchronous JavaScript and XML ,异步的javascript和XML 5.2.使用ajax有什么用 数据交互,可以从服务器获取 ...
- 玩转SpringBoot之捣鼓 Redis
我们都知道,把首页数据放到Redis里,能够加快首页数据的访问速度.但是我们要如何准确又快速的将 Redis 整合到自己的 SpringBoot2.x 项目中呢?今天阿淼就带大家爬一爬其中的门门道道. ...
- python用类的方式创建线程---自创建类
用类的方式创建线程---自创建类 import threadingimport time class MyThread(threading.Thread):#自建MyThread类继承threadin ...
- 面向切面对象AOP
前言 面向切面编程(思想)AOP Aspect Oriented Programming,是面向对象基础上 更关注最终目标 而不关注中间的小目标,简而言之,就是我们的目标(例如constroller ...
- Netty 源码解析(二):Netty 的 Channel
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...
- 每日一题 - 剑指 Offer 32 - I. 从上到下打印二叉树
题目信息 时间: 2019-06-25 题目链接:Leetcode tag:BFS(广度优先搜索) 队列 难易程度:中等 题目描述: 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印 ...