Linux常见上下文切换---理论篇 - Go语言中文社区

Linux常见上下文切换---理论篇


文章图片来源网络,侵权联系删除.

一、CPU上下文:

  • PC寄存器:存放着CPU正在执行的指令或者CPU将要执行的指令;
  • 其他寄存器,CPU身边速度极快的内存,保存着其他重要的信息。
    在这里插入图片描述

CPU上下文切换:由于任务(进程、线程或者中断)在CPU上执行时宏观看上去所有进程都在执行,实际上任务不可能独占CPU,是需要遵守CPU的调度算法,这个过程中任务是要不停的换进换出,而上下文指的就是这个某个任务执行所依赖的环境,这些信息是要被换进CPU或者换出CPU的。

往往上下文的切换是比较耗时的,理想状态是更多时间花在任务的运行上,而不是上下文切换。

以下是CPU上下文切换的几个场景:

二、系统调用带来的上下文切换:

  • 内核态:(Ring 0)执行内核代码,就是陷入内核,处于内核态。具有最高的权限,可以访问计算机的所有资源;

  • 用户态:(Ring 3)执行用户的空间的代码,就是处于内核态;访问资源受限,需要时必须经过陷入内核,才能访问特权资源。
    在这里插入图片描述
    系统调用: 我们知道,内核帮我们管理着所有的宝贵的硬件资源,当我们的上层应用程序需要一些硬件资源时,不能直接去取,而是需要通过操作系统预留给我们的接口获取。这个时候执行我们用户态的代码已经不能满足了,此时将陷入内核,执行操作系统的代码,有操作系统去获取资源,然后以某种形式返回给我们用户,这就是系统调用的大概过程。系统调用详解
    系统调用的上下文切换:
    CPU正在执行我们的用户态代码,执行到系统调用时,触发中断,CPU将从用户态转到内核态,执行内核态代码,有没有想过CPU陷入内核前做了什么,执行完毕后又做了什么?

  • 陷入内核前:保存当前进程的堆栈地址入栈保存,比如在用户态执行的位置等。然后将CPU寄存器的内容更新要执行的内核指令的内容,最后陷入内核,执行内核代码;

  • 返回用户空间前:需要将内核给用户空间返回的资源,如句柄、文件描述符等保存下来,然后将CPU寄存器的内容更新为陷入内核前被压入栈的信息,然后接着执行用户态的指令。

系统调用是在同一个进程中完成的,区别与进程之间的切换
一次系统调用需要进行两次的CPU上下文切换,实际上花费的时间是很可观的

三、进程切换带来的CPU上下文切换:

我们考虑两种进程:1.正在CPU上运行的进程,2.等待上CPU去运行的进程。
Linux为每一个CPU维护一个就绪队列,将活跃进程(正在运行和正在等待CPU)按照优先级和等待CPU时间进行排序,选择最需要CPU的进程,即优先级较高和等待时间较长的进程使用CPU。
进程切换的几种常见的场景:

  1. 每个进程在CPU上执行时间被划分为好多块时间片,当某一次执行时对应的时间片用完了,这个进程就要被换下来,由调度程序根据进程的优先级和已经等待的时间决策,接下来哪个进程使用CPU;
  2. 进程运行时,发现系统的资源不够了,这个时候也会被挂起,被换下来,依然由调度程序根据进程的优先级和已经等待的时间决策,接下来哪个进程使用CPU;
  3. 当进程调用sleep等睡眠函数的时候,将自己主动的挂起,会被换下来,由调度程序根据进程的优先级和已经等待的时间决策,接下来哪个进程使用CPU;
  4. 此时有个优先级更高的进程需要紧急执行,此时当前CPU上的进程会被换下来;
  5. 发生硬件中断,当前CPU上的进程会被挂起,去执行对应的中断处理程序。

进程切换时做了什么:

  • 先保存当前进程的虚拟内存、栈等信息,然后保存当前进程的内核状态和CPU寄存器内容。
  • 然后加载下一个进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

TLB:(Translation Lookaside Buffer)
TLB是Linux管理虚拟内存到物理内存的映射关系的。当虚拟内存更新以后,TLB也需要刷新,内存的访问也会随之变慢。
缓存刷新:
在多处理器系统上,缓存是被多个处理器共享的,刷新缓存不仅会影响当前处理器的进程,还会影响影响共享缓存上的其他处理器的进程。

进程频繁的切换,进程上下文的更新时间/CPU真正运行进程时间 比率会上升
使得缓存失效,命中率降低,缓存的内容也是要更新的,缓存失效可以说是很致命的

现在编程有一种手段就是禁止当前进程被换出CPU,这种手段说好也好,说不好也不好。

四、线程的上下文切换:

线程的历史:
在Linux上早期是没有线程这么一说的,后来可能是向其他操作系统看齐,出现了轻量级进程–也就是我们所看到的thread。所以,线程被看作是进程内的一条执行路径,不过当前进程内的所有线程是共享进程的数据、堆等资源的,线程有的只不过是本身所需的栈内存和一些寄存器罢了。
从这里也体现了我们经常说的一句话:“进程是资源分配的基本单位,线程是CPU调度的基本单位”。

所谓的内核调度:实际上是线程的调度,单进程就是一个线程,此时划等号
进程只不过是给线程提供虚拟内存、共享数据等资源而已

线程切换的几个场景:

  1. 同一个进程内的线程切换,由于两个线程共享的是同一个进程的资源,所以大多数的资源是不需要切换的,需要切换的只是线程独有的栈数据和一些寄存器;
  2. 不同进程的两个线程,此时等同于前面说到的进程的切换。
当我们使用多线程编程时,同一个进程内的线程切换,耗时明显比多进程切换少得多
这也是为什么说多线程编程比多进程编程效率高的原因。

但是,多线程的健壮性堪忧,如何抉择,还是得考虑实时的场景。

五、中断上下文的切换:

还有一种情况,就是为了快速响应硬件的事件,中断处理会打断进程的正常运行和调度,从而转到中断处理程序,来处理设备的事件。被打断的进程在换下来之前需要讲当前的状态保存下来,这样在中断结束以后恢复原来的状态。

中断上下文内容:

内核态中断服务程序执行所需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等

由于中断会打断正常进程的调度和执行,所以大部分的中断处理程序都短小精悍,以便尽可能的执行完毕。
不过,跟进程上下文切换一样,中断上下文的切换也需要消耗CPU,切换次数过多也会耗费大量的CPU。甚至严重降低系统的整体性能,所以,中断次数过多的时,需要考虑中断是否给我们的系统带来严重的性能问题。

六、总结:

  1. CPU上下文切换,时保证LInux系统正常工作的核心功能之一,一般情况下不需要我们特别的关注;
  2. 但是过多的上下文切换,回把CPU事件消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正的运行时间,导致系统的性能大幅度的下降。

参考:极客–linux性能优化.

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/KingOfMyHeart/article/details/100051685
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 22:02:50
  • 阅读 ( 1298 )
  • 分类:Linux

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢