社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
文章图片来源网络,侵权联系删除.
一、CPU上下文:
CPU上下文切换:由于任务(进程、线程或者中断)在CPU上执行时宏观看上去所有进程都在执行,实际上任务不可能独占CPU,是需要遵守CPU的调度算法,这个过程中任务是要不停的换进换出,而上下文指的就是这个某个任务执行所依赖的环境,这些信息是要被换进CPU或者换出CPU的。
往往上下文的切换是比较耗时的,理想状态是更多时间花在任务的运行上,而不是上下文切换。
以下是CPU上下文切换的几个场景:
二、系统调用带来的上下文切换:
内核态:(Ring 0)执行内核代码,就是陷入内核,处于内核态。具有最高的权限,可以访问计算机的所有资源;
用户态:(Ring 3)执行用户的空间的代码,就是处于内核态;访问资源受限,需要时必须经过陷入内核,才能访问特权资源。
系统调用: 我们知道,内核帮我们管理着所有的宝贵的硬件资源,当我们的上层应用程序需要一些硬件资源时,不能直接去取,而是需要通过操作系统预留给我们的接口获取。这个时候执行我们用户态的代码已经不能满足了,此时将陷入内核,执行操作系统的代码,有操作系统去获取资源,然后以某种形式返回给我们用户,这就是系统调用的大概过程。系统调用详解
系统调用的上下文切换:
CPU正在执行我们的用户态代码,执行到系统调用时,触发中断,CPU将从用户态转到内核态,执行内核态代码,有没有想过CPU陷入内核前做了什么,执行完毕后又做了什么?
陷入内核前:保存当前进程的堆栈地址入栈保存,比如在用户态执行的位置等。然后将CPU寄存器的内容更新要执行的内核指令的内容,最后陷入内核,执行内核代码;
返回用户空间前:需要将内核给用户空间返回的资源,如句柄、文件描述符等保存下来,然后将CPU寄存器的内容更新为陷入内核前被压入栈的信息,然后接着执行用户态的指令。
系统调用是在同一个进程中完成的,区别与进程之间的切换
一次系统调用需要进行两次的CPU上下文切换,实际上花费的时间是很可观的
三、进程切换带来的CPU上下文切换:
Linux为每一个CPU维护一个就绪队列,将活跃进程(正在运行和正在等待CPU)按照优先级和等待CPU时间进行排序,选择最需要CPU的进程,即优先级较高和等待时间较长的进程使用CPU。
进程切换的几种常见的场景:
进程切换时做了什么:
TLB:(Translation Lookaside Buffer)
TLB是Linux管理虚拟内存到物理内存的映射关系的。当虚拟内存更新以后,TLB也需要刷新,内存的访问也会随之变慢。
缓存刷新:
在多处理器系统上,缓存是被多个处理器共享的,刷新缓存不仅会影响当前处理器的进程,还会影响影响共享缓存上的其他处理器的进程。
进程频繁的切换,进程上下文的更新时间/CPU真正运行进程时间 比率会上升
使得缓存失效,命中率降低,缓存的内容也是要更新的,缓存失效可以说是很致命的
现在编程有一种手段就是禁止当前进程被换出CPU,这种手段说好也好,说不好也不好。
四、线程的上下文切换:
线程的历史:
在Linux上早期是没有线程这么一说的,后来可能是向其他操作系统看齐,出现了轻量级进程–也就是我们所看到的thread。所以,线程被看作是进程内的一条执行路径,不过当前进程内的所有线程是共享进程的数据、堆等资源的,线程有的只不过是本身所需的栈内存和一些寄存器罢了。
从这里也体现了我们经常说的一句话:“进程是资源分配的基本单位,线程是CPU调度的基本单位”。
所谓的内核调度:实际上是线程的调度,单进程就是一个线程,此时划等号
进程只不过是给线程提供虚拟内存、共享数据等资源而已
线程切换的几个场景:
当我们使用多线程编程时,同一个进程内的线程切换,耗时明显比多进程切换少得多
这也是为什么说多线程编程比多进程编程效率高的原因。
但是,多线程的健壮性堪忧,如何抉择,还是得考虑实时的场景。
五、中断上下文的切换:
还有一种情况,就是为了快速响应硬件的事件,中断处理会打断进程的正常运行和调度,从而转到中断处理程序,来处理设备的事件。被打断的进程在换下来之前需要讲当前的状态保存下来,这样在中断结束以后恢复原来的状态。
中断上下文内容:
内核态中断服务程序执行所需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等
由于中断会打断正常进程的调度和执行,所以大部分的中断处理程序都短小精悍,以便尽可能的执行完毕。
不过,跟进程上下文切换一样,中断上下文的切换也需要消耗CPU,切换次数过多也会耗费大量的CPU。甚至严重降低系统的整体性能,所以,中断次数过多的时,需要考虑中断是否给我们的系统带来严重的性能问题。
六、总结:
参考:极客–linux性能优化.
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!