你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读
上下文切换(Context Switching) 是计算机系统中非常重要的一个概念,尤其在涉及多任务或多进程操作的操作系统中,常常会出现上下文切换的情况。它指的是操作系统在多个任务(进程或线程)之间切换时,保存当前任务的状态并恢复另一个任务的状态。
上下文切换指的是操作系统将当前正在执行的进程或线程的执行状态(即“上下文”)保存下来,然后加载下一个要执行的进程或线程的执行状态。
进程上下文:包括 CPU 寄存器值、程序计数器、堆栈指针、进程状态等。
线程上下文:线程上下文相较于进程上下文更加轻量,通常只包括寄存器值、程序计数器和栈指针等。
上下文切换通常发生在以下几种情况:
时间片耗尽:时间片轮转调度时,当前线程的时间片到期,需要切换到另一个线程。
I/O 阻塞:当前进程等待 I/O 操作时,操作系统会将其暂停并切换到其他进程执行。
高优先级任务:如果有高优先级的任务到来,操作系统可能会中断当前任务,进行上下文切换,优先执行高优先级任务。
进程挂起:进程因某些原因挂起时,操作系统会进行上下文切换。
上下文切换的基本过程如下:
保存当前任务的状态:
操作系统首先将当前正在执行的进程(或线程)的所有状态保存到进程控制块(PCB)或线程控制块(TCB)中。这些状态包括:CPU 寄存器、程序计数器(PC)、堆栈指针、程序状态字(PSW)等。
调度下一个任务:
操作系统选择一个新的进程或线程进行执行,通常是根据调度算法选择一个任务(例如,时间片轮转、优先级调度等)。
恢复下一个任务的状态:
操作系统从新任务的 PCB 或 TCB 中恢复其执行状态,包括 CPU 寄存器、程序计数器、堆栈指针等。
执行新的任务:
系统将控制权交给新的进程或线程,开始执行其指令。
上下文切换是计算机系统中非常常见的操作,但它也会带来一定的代价。上下文切换的代价主要体现在以下几个方面:
CPU 寄存器的保存与恢复:
每次切换时,都需要将当前任务的寄存器值保存到内存中,并将下一个任务的寄存器值从内存中恢复到 CPU 中。这个过程消耗了 CPU 时间。
内存管理的开销:
上下文切换可能涉及到虚拟内存的管理,如进程切换时可能需要加载新的页表,或者切换到不同的内存空间。
缓存失效:
上下文切换时,当前任务的缓存内容会被丢弃,新的任务可能无法立即利用缓存中的数据,这会导致缓存失效,影响性能。
中断处理和调度开销:
上下文切换通常需要中断处理程序来保存和恢复上下文,同时进行调度计算,这些操作本身会消耗时间。
综上所述,上下文切换会增加系统的运行开销,影响系统的吞吐量和响应时间。
为了减少上下文切换的开销,操作系统通常会采用以下一些优化方法:
减少不必要的上下文切换:
避免过于频繁的上下文切换,例如通过合理调整进程的时间片大小、调度策略等。
使用 协程(coroutines) 或 轻量级线程 等技术,减少线程的创建和销毁频率,从而减少上下文切换。
线程和进程的亲和性(CPU Affinity):
将任务绑定到特定的 CPU 上,这样可以避免任务在不同 CPU 核心之间频繁切换,减少缓存失效。
提高系统的调度效率:
通过优化调度算法,如 多级反馈队列调度,使得高优先级任务更快地获得 CPU 资源,减少不必要的切换。
减少 I/O 阻塞:
使用异步 I/O 或事件驱动模型,减少进程因等待 I/O 而发生的上下文切换。
线程上下文切换:线程之间共享同一进程的地址空间,因此,线程之间的上下文切换仅涉及到少量的状态保存和恢复,代价较低。
频繁的上下文切换会导致性能下降,尤其是在任务粒度较小的情况下,每个任务占用的 CPU 时间较短时,系统的上下文切换次数会显著增加,导致性能瓶颈。因此,在设计并发程序时,应该尽量避免频繁的上下文切换。
假设系统中有多个线程竞争 CPU 资源,如果频繁发生上下文切换,每个线程无法长时间占用 CPU 进行计算,反而浪费了大量的时间进行状态保存和恢复,导致 CPU 利用率下降,系统的总体性能受到影响。
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!