Linux CPU性能优化 —— 上下文切换对性能的影响

写在前面:
由于之前在开发分布式系统中由于云服务器性能原因,导致系统总是断连等错误。但是之前一般只是简单gdb调试一下,定位错误异常艰难,所以决定开设此专栏,系统的记录我学习Linux 性能优化的历程。

作者邮箱:2107810343@qq.com
时间:2021/04/28 21:10
实现环境:Linux
系统:ubuntu 18.04

CPU上下文切换

什么是CPU上下文

在每个任务运行前,CPU都需要之道任务从哪儿加载,又从哪儿运行,也就是说,需要系统实现帮它设置好CPU寄存器程序计数器。它们都是CPU在运行任何任务前,必依赖的运行环境,因此也称之为CPU上下文

  • CPU寄存器:CPU内置的容量小,但速度极快的内存。
  • 程序计数器:用来存储CPU正在执行的指令位置,或者即将执行的下一条指令位置。

在这里插入图片描述

CPU上下文切换,就是先把前一个任务的CPU上下文保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后在跳转到程序计数器所指的新位置,运行新任务。而保存的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。

而根据任务的不同,CPU上下文切换还分为进程上下文切换,线程上下文切换和中断上下文切换。这些除了执行保存寄存器和程序计数器的步骤,各自还有自己不同的事情要处理。

进程上下文切换

进程的运行空间分为用户态和内核态。其分别对应着下图CPU特权等级ring0和ring3。

  • 内核态:具有最高权限,可以直接访问所有资源
  • 用户态:只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入内核中,才能访问这些资源

在这里插入图片描述

关于ring1和ring2
这里直接引用StackOverflow上面大佬的回复:
As a hobbyist operating system writer, I found that because paging (a major part of the modern protection model) only has a concept of privileged (ring 0,1,2) and unprivileged, the benefit to rings 1 and 2 were diminished greatly.
The intent by Intel in having rings 1 and 2 is for the OS to put device drivers at that level, so they are privileged, but somewhat separated from the rest of the kernel code.
Rings 1 and 2 are in a way, “mostly” privileged. They can access supervisor pages, but if they attempt to use a privileged instruction, they still GPF like ring 3 would. So it is not a bad place for drivers as Intel planned…
That said, they definitely do have use in some designs. In fact, not always directly by the OS. For example, VirtualBox, a Virtual Machine, puts the guest kernel code in ring 1. I am also sure some operating systems do make use of them, I just don’t think it is a popular design at the moment.
大概意思就是,对于分页管理的Linux来说,只有非特权(ring3)和特权(ring0 1 2)之分,ring1和ring2用的很少。intel将操作系统的设备驱动程序置于该级别,使其拥有大部分的特权,但不是全部。

而从用户态到内核态,需要经过系统调用来完成。一次系统调用过程会发生两次CPU上下文的切换。不过这里其实并不设计到虚拟内存等进程用户态的资源,即不会切换进程。也就是不会发生进程的切换

进程上下文切换

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

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

线程上下文切换

线程的上下文切换主要分两种情况:

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

中断上下文切换

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

跟进程上下文切换不同的是,中断上下文的切换其实并不涉及进程的用户态中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括内核态中断服务程序执行所必需的状态,包括CPU寄存器、内核堆栈、硬件中断参数。对于同一个CPU来说,中断处理比进程拥有更高的优先级,所以中断上下文切换并不会与进程上下文切换同时发生。

查看系统上下文切换情况:vmstat和pidstat

现在我们知道了,过多的上下文切换,会把CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正的运行时间,造成系统性能大幅下降

我们可以在Linux中用 vmstat 来查看系统上下文切换情况:
在这里插入图片描述
我们需要在这里重点关注一下几个字段:

  • cs:每秒上下文切换的次数
  • in:每秒中断的次数
  • r:就绪队列的长度,也就是正在运行和等待CPU的进程数
  • b:处于不可中断状态等待进程数

vmstat 只给出了系统总体的上下文切换情况,想要查看每个进程的详细详情,就需要使用 pidstat,加上 -w选项,就可以查看进程上下文的情况。
在这里插入图片描述
这里我们关注以下两个字段:

  • cswch:每秒自愿上下文切换的次数
  • nvcswch:每秒非自愿上下文切换的次数

解释
自愿上下文切换:指进程无法获取所需资源,导致的上下文切换
非自愿上下文切换:指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换

高负载模拟与排查:sysbench和pidstat

这次使用的工具是sysbench,是一个多线程的基准测试工具,用来评估不同参数下的数据库负载情况。

先安装一下:

ubuntu@VM-0-2-ubuntu:~/ByteTalk/LogServer$ sudo apt install sysbench

然后开始进行多线程模拟:

ubuntu@VM-0-2-ubuntu:~/ByteTalk/LogServer$ sysbench --threads=10 --max-time=300 threads run

使用vmstat查看上下文切换情况:
在这里插入图片描述
可以看到,每秒中断数高达2w,上下文切换数更是达到了94w。

然后我们可以使用pidstat命令查看是哪个进程引起的:

ubuntu@VM-0-2-ubuntu:~/ByteTalk/UserService$ pidstat -wt 1

在这里插入图片描述
很明显,就是sysbench

参考文献

[1] 倪朋飞.Linux性能优化实战.极客时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenmingik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值