这是"操作系统系列教程"的第十九篇。前面几篇我们讲了进程调度、时间片轮转、优先级调度,知道了操作系统如何在多个进程之间分配 CPU。但还有一个关键问题没有回答:当一个进程被换下、另一个进程被换上时,CPU 到底做了什么?那些被中断的进程,它们的执行状态是怎么保存的?答案就是------上下文切换。
一个"接电话"的类比
你正在写代码,突然手机响了。你放下代码,接起电话,聊了十分钟,挂断电话,然后回到电脑前,从刚才停下的地方继续写。
这个过程中,你的大脑做了几件事:
- 记住当前进度:刚才写到哪一行、光标在哪个位置、下一步想写什么;
- 保存思维状态:刚才思考的思路、正在调试的 bug、脑海里的一些临时变量;
- 切换到电话模式:开始和对方交流;
- 恢复之前的进度:挂断电话后,回到刚才写到的位置,继续思考。
操作系统在切换进程时,做的事情几乎一模一样。它需要保存当前进程的执行状态,恢复下一个进程的执行状态。这个过程,就是上下文切换(Context Switch)。
上下文切换是多任务操作系统的核心机制。没有它,多个进程就无法"同时"运行;但切换太频繁,又会带来巨大的性能开销。理解上下文切换,是理解操作系统并发执行的关键。
这篇文章,我们就来把"上下文切换"这件事讲清楚。
说明:本文涉及寄存器、程序计数器、页表、TLB 等概念。初学者如果遇到暂时看不懂的术语,可以先跳过细节,重点理解"上下文切换就是保存当前进程状态、恢复下一个进程状态的过程"这一核心思想。
一、上下文切换到底是什么?
1.1 从"上下文"说起
要理解上下文切换,先要理解什么是上下文(Context)。
上下文是一个进程在某一时刻的执行状态,包括:
- 程序执行到了哪里(程序计数器);
- 正在使用哪些数据(寄存器的值);
- 栈的情况(栈指针、栈内容);
- 内存映射(页表基址);
- 打开的文件(文件描述符表);
- 信号处理状态等。
简单来说,上下文就是进程当前运行状态的全部信息。有了这些信息,进程就可以在任意时刻被暂停,并在稍后被恢复,就像从未被中断过一样。
1.2 上下文切换的定义
给出一个正式的定义:
上下文切换(Context Switch)是操作系统在多任务环境中,把 CPU 从一个进程(或线程)切换到另一个进程(或线程)时,保存当前进程的上下文、恢复下一个进程的上下文的过程。
这个定义里有三个关键点:
- 保存当前进程的上下文:把当前进程的执行状态存储起来;
- 恢复下一个进程的上下文:把下一个进程之前保存的执行状态加载回来;
- 切换 CPU 的执行流:CPU 从执行一个进程变为执行另一个进程。
1.3 上下文切换在系统中的位置
#mermaid-svg-civqToQbGV1ZM5L6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-civqToQbGV1ZM5L6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-civqToQbGV1ZM5L6 .error-icon{fill:#552222;}#mermaid-svg-civqToQbGV1ZM5L6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-civqToQbGV1ZM5L6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-civqToQbGV1ZM5L6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-civqToQbGV1ZM5L6 .marker.cross{stroke:#333333;}#mermaid-svg-civqToQbGV1ZM5L6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-civqToQbGV1ZM5L6 p{margin:0;}#mermaid-svg-civqToQbGV1ZM5L6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-civqToQbGV1ZM5L6 .cluster-label text{fill:#333;}#mermaid-svg-civqToQbGV1ZM5L6 .cluster-label span{color:#333;}#mermaid-svg-civqToQbGV1ZM5L6 .cluster-label span p{background-color:transparent;}#mermaid-svg-civqToQbGV1ZM5L6 .label text,#mermaid-svg-civqToQbGV1ZM5L6 span{fill:#333;color:#333;}#mermaid-svg-civqToQbGV1ZM5L6 .node rect,#mermaid-svg-civqToQbGV1ZM5L6 .node circle,#mermaid-svg-civqToQbGV1ZM5L6 .node ellipse,#mermaid-svg-civqToQbGV1ZM5L6 .node polygon,#mermaid-svg-civqToQbGV1ZM5L6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-civqToQbGV1ZM5L6 .rough-node .label text,#mermaid-svg-civqToQbGV1ZM5L6 .node .label text,#mermaid-svg-civqToQbGV1ZM5L6 .image-shape .label,#mermaid-svg-civqToQbGV1ZM5L6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-civqToQbGV1ZM5L6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-civqToQbGV1ZM5L6 .rough-node .label,#mermaid-svg-civqToQbGV1ZM5L6 .node .label,#mermaid-svg-civqToQbGV1ZM5L6 .image-shape .label,#mermaid-svg-civqToQbGV1ZM5L6 .icon-shape .label{text-align:center;}#mermaid-svg-civqToQbGV1ZM5L6 .node.clickable{cursor:pointer;}#mermaid-svg-civqToQbGV1ZM5L6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-civqToQbGV1ZM5L6 .arrowheadPath{fill:#333333;}#mermaid-svg-civqToQbGV1ZM5L6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-civqToQbGV1ZM5L6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-civqToQbGV1ZM5L6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-civqToQbGV1ZM5L6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-civqToQbGV1ZM5L6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-civqToQbGV1ZM5L6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-civqToQbGV1ZM5L6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-civqToQbGV1ZM5L6 .cluster text{fill:#333;}#mermaid-svg-civqToQbGV1ZM5L6 .cluster span{color:#333;}#mermaid-svg-civqToQbGV1ZM5L6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-civqToQbGV1ZM5L6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-civqToQbGV1ZM5L6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-civqToQbGV1ZM5L6 .icon-shape,#mermaid-svg-civqToQbGV1ZM5L6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-civqToQbGV1ZM5L6 .icon-shape p,#mermaid-svg-civqToQbGV1ZM5L6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-civqToQbGV1ZM5L6 .icon-shape .label rect,#mermaid-svg-civqToQbGV1ZM5L6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-civqToQbGV1ZM5L6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-civqToQbGV1ZM5L6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-civqToQbGV1ZM5L6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进程B
内核
进程A
执行中
被中断
保存进程A的上下文
选择进程B
恢复进程B的上下文
被恢复
继续执行
图 1:上下文切换在系统中的位置。 内核负责保存和恢复上下文,调度器负责选择下一个进程。
从图 1 可以看出,上下文切换发生在内核中。进程本身并不知道自己被切换了,它只知道自己被暂停了一下,然后继续执行。
二、上下文切换时到底保存了什么?
这是本文的核心问题。上下文切换时,操作系统需要保存哪些信息?
2.1 CPU 寄存器
CPU 寄存器是进程执行时最核心的状态。包括:
| 寄存器类型 | 作用 |
|---|---|
| 通用寄存器 | 存储临时数据、函数参数、返回值 |
| 程序计数器(PC) | 下一条要执行的指令地址 |
| 栈指针(SP) | 当前栈顶的位置 |
| 基址指针(BP) | 当前栈帧的基址 |
| 程序状态字(PSW) | 标志位、特权级、中断使能等 |
这些寄存器的值决定了进程执行到哪里、正在处理什么数据。保存了这些寄存器,就等于保存了进程的"执行现场"。
2.2 程序计数器(PC)
程序计数器(Program Counter,PC)是最关键的寄存器之一。它存储着下一条要执行的指令的地址。
当进程被中断时,PC 指向的是下一条尚未执行的指令。恢复时,CPU 从 PC 指向的地址继续执行,就好像从未被中断过。
#mermaid-svg-auHuOot5Tq6UwLzu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-auHuOot5Tq6UwLzu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-auHuOot5Tq6UwLzu .error-icon{fill:#552222;}#mermaid-svg-auHuOot5Tq6UwLzu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-auHuOot5Tq6UwLzu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-auHuOot5Tq6UwLzu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-auHuOot5Tq6UwLzu .marker.cross{stroke:#333333;}#mermaid-svg-auHuOot5Tq6UwLzu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-auHuOot5Tq6UwLzu p{margin:0;}#mermaid-svg-auHuOot5Tq6UwLzu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-auHuOot5Tq6UwLzu .cluster-label text{fill:#333;}#mermaid-svg-auHuOot5Tq6UwLzu .cluster-label span{color:#333;}#mermaid-svg-auHuOot5Tq6UwLzu .cluster-label span p{background-color:transparent;}#mermaid-svg-auHuOot5Tq6UwLzu .label text,#mermaid-svg-auHuOot5Tq6UwLzu span{fill:#333;color:#333;}#mermaid-svg-auHuOot5Tq6UwLzu .node rect,#mermaid-svg-auHuOot5Tq6UwLzu .node circle,#mermaid-svg-auHuOot5Tq6UwLzu .node ellipse,#mermaid-svg-auHuOot5Tq6UwLzu .node polygon,#mermaid-svg-auHuOot5Tq6UwLzu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-auHuOot5Tq6UwLzu .rough-node .label text,#mermaid-svg-auHuOot5Tq6UwLzu .node .label text,#mermaid-svg-auHuOot5Tq6UwLzu .image-shape .label,#mermaid-svg-auHuOot5Tq6UwLzu .icon-shape .label{text-anchor:middle;}#mermaid-svg-auHuOot5Tq6UwLzu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-auHuOot5Tq6UwLzu .rough-node .label,#mermaid-svg-auHuOot5Tq6UwLzu .node .label,#mermaid-svg-auHuOot5Tq6UwLzu .image-shape .label,#mermaid-svg-auHuOot5Tq6UwLzu .icon-shape .label{text-align:center;}#mermaid-svg-auHuOot5Tq6UwLzu .node.clickable{cursor:pointer;}#mermaid-svg-auHuOot5Tq6UwLzu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-auHuOot5Tq6UwLzu .arrowheadPath{fill:#333333;}#mermaid-svg-auHuOot5Tq6UwLzu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-auHuOot5Tq6UwLzu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-auHuOot5Tq6UwLzu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-auHuOot5Tq6UwLzu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-auHuOot5Tq6UwLzu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-auHuOot5Tq6UwLzu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-auHuOot5Tq6UwLzu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-auHuOot5Tq6UwLzu .cluster text{fill:#333;}#mermaid-svg-auHuOot5Tq6UwLzu .cluster span{color:#333;}#mermaid-svg-auHuOot5Tq6UwLzu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-auHuOot5Tq6UwLzu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-auHuOot5Tq6UwLzu rect.text{fill:none;stroke-width:0;}#mermaid-svg-auHuOot5Tq6UwLzu .icon-shape,#mermaid-svg-auHuOot5Tq6UwLzu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-auHuOot5Tq6UwLzu .icon-shape p,#mermaid-svg-auHuOot5Tq6UwLzu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-auHuOot5Tq6UwLzu .icon-shape .label rect,#mermaid-svg-auHuOot5Tq6UwLzu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-auHuOot5Tq6UwLzu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-auHuOot5Tq6UwLzu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-auHuOot5Tq6UwLzu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指令 1
指令 2
指令 3
指令 4
被中断
下一条指令
PC 指向这里
图 2:程序计数器的作用。 进程被中断时,PC 指向下一条未执行的指令。
2.3 栈指针(SP)
栈是进程存放局部变量、函数参数、返回地址的地方。栈指针(Stack Pointer,SP)指向当前栈顶。
当进程被中断时,栈中可能有多层函数调用的信息。保存 SP,就能在恢复时找到正确的栈位置。
2.4 内存管理信息
进程的内存管理信息也需要保存,包括:
- 页表基址:指向该进程的页表;
- 内存限制:代码段、数据段、堆、栈的边界。
在切换进程时,操作系统需要切换页表,让 CPU 使用新进程的地址空间。这也是上下文切换开销大的一个重要原因。
2.5 其他状态
除了上述核心状态,还需要保存:
| 状态 | 说明 |
|---|---|
| 文件描述符表 | 进程打开的文件 |
| 信号处理状态 | 信号处理函数的地址 |
| 进程状态 | 就绪、运行、阻塞 |
| 调度信息 | 优先级、时间片剩余 |
2.6 保存到哪里?
这些上下文信息保存在哪里?答案是进程控制块(PCB)。
每个进程有一个 PCB,操作系统在切换进程时,把当前进程的上下文保存到它的 PCB 中,然后从下一个进程的 PCB 中恢复上下文。
#mermaid-svg-nZCo6Kyx7WEtkJ63{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .error-icon{fill:#552222;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .marker.cross{stroke:#333333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 p{margin:0;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster-label text{fill:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster-label span{color:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster-label span p{background-color:transparent;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .label text,#mermaid-svg-nZCo6Kyx7WEtkJ63 span{fill:#333;color:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .node rect,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node circle,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node ellipse,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node polygon,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .rough-node .label text,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node .label text,#mermaid-svg-nZCo6Kyx7WEtkJ63 .image-shape .label,#mermaid-svg-nZCo6Kyx7WEtkJ63 .icon-shape .label{text-anchor:middle;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .rough-node .label,#mermaid-svg-nZCo6Kyx7WEtkJ63 .node .label,#mermaid-svg-nZCo6Kyx7WEtkJ63 .image-shape .label,#mermaid-svg-nZCo6Kyx7WEtkJ63 .icon-shape .label{text-align:center;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .node.clickable{cursor:pointer;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .arrowheadPath{fill:#333333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nZCo6Kyx7WEtkJ63 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nZCo6Kyx7WEtkJ63 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster text{fill:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .cluster span{color:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nZCo6Kyx7WEtkJ63 rect.text{fill:none;stroke-width:0;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .icon-shape,#mermaid-svg-nZCo6Kyx7WEtkJ63 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .icon-shape p,#mermaid-svg-nZCo6Kyx7WEtkJ63 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .icon-shape .label rect,#mermaid-svg-nZCo6Kyx7WEtkJ63 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nZCo6Kyx7WEtkJ63 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nZCo6Kyx7WEtkJ63 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nZCo6Kyx7WEtkJ63 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进程B的PCB
进程A的PCB
保存
恢复
寄存器值
程序计数器
栈指针
页表基址
寄存器值
程序计数器
栈指针
页表基址
内核
图 3:上下文保存在 PCB 中。 切换时,从当前进程的 PCB 保存,从下一个进程的 PCB 恢复。
三、上下文切换的类型
上下文切换并不是只有一种。根据切换的对象不同,可以分为几种类型。
3.1 进程上下文切换
进程上下文切换是最完整、开销最大的切换。它不仅需要保存和恢复 CPU 寄存器,还需要切换页表、刷新 TLB、切换内存地址空间。
进程上下文切换的开销包括:
- 保存和恢复寄存器;
- 切换页表;
- 刷新 TLB;
- 刷新 CPU 缓存(部分架构)。
3.2 线程上下文切换
线程上下文切换发生在同一进程内的两个线程之间。由于同一进程的线程共享地址空间,线程切换不需要切换页表,也不需要刷新 TLB,因此开销比进程切换小得多。
| 对比项 | 进程切换 | 线程切换 |
|---|---|---|
| 保存寄存器 | 需要 | 需要 |
| 切换页表 | 需要 | 不需要 |
| 刷新 TLB | 需要 | 不需要 |
| 开销 | 大 | 小 |
#mermaid-svg-CiLcdDII5YWpjAc4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CiLcdDII5YWpjAc4 .error-icon{fill:#552222;}#mermaid-svg-CiLcdDII5YWpjAc4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CiLcdDII5YWpjAc4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CiLcdDII5YWpjAc4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CiLcdDII5YWpjAc4 .marker.cross{stroke:#333333;}#mermaid-svg-CiLcdDII5YWpjAc4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CiLcdDII5YWpjAc4 p{margin:0;}#mermaid-svg-CiLcdDII5YWpjAc4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster-label text{fill:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster-label span{color:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster-label span p{background-color:transparent;}#mermaid-svg-CiLcdDII5YWpjAc4 .label text,#mermaid-svg-CiLcdDII5YWpjAc4 span{fill:#333;color:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 .node rect,#mermaid-svg-CiLcdDII5YWpjAc4 .node circle,#mermaid-svg-CiLcdDII5YWpjAc4 .node ellipse,#mermaid-svg-CiLcdDII5YWpjAc4 .node polygon,#mermaid-svg-CiLcdDII5YWpjAc4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CiLcdDII5YWpjAc4 .rough-node .label text,#mermaid-svg-CiLcdDII5YWpjAc4 .node .label text,#mermaid-svg-CiLcdDII5YWpjAc4 .image-shape .label,#mermaid-svg-CiLcdDII5YWpjAc4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-CiLcdDII5YWpjAc4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CiLcdDII5YWpjAc4 .rough-node .label,#mermaid-svg-CiLcdDII5YWpjAc4 .node .label,#mermaid-svg-CiLcdDII5YWpjAc4 .image-shape .label,#mermaid-svg-CiLcdDII5YWpjAc4 .icon-shape .label{text-align:center;}#mermaid-svg-CiLcdDII5YWpjAc4 .node.clickable{cursor:pointer;}#mermaid-svg-CiLcdDII5YWpjAc4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CiLcdDII5YWpjAc4 .arrowheadPath{fill:#333333;}#mermaid-svg-CiLcdDII5YWpjAc4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CiLcdDII5YWpjAc4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CiLcdDII5YWpjAc4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiLcdDII5YWpjAc4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CiLcdDII5YWpjAc4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiLcdDII5YWpjAc4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster text{fill:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 .cluster span{color:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CiLcdDII5YWpjAc4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CiLcdDII5YWpjAc4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-CiLcdDII5YWpjAc4 .icon-shape,#mermaid-svg-CiLcdDII5YWpjAc4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiLcdDII5YWpjAc4 .icon-shape p,#mermaid-svg-CiLcdDII5YWpjAc4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CiLcdDII5YWpjAc4 .icon-shape .label rect,#mermaid-svg-CiLcdDII5YWpjAc4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiLcdDII5YWpjAc4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CiLcdDII5YWpjAc4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CiLcdDII5YWpjAc4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程切换
保存寄存器
恢复寄存器
进程切换
保存寄存器
切换页表
刷新 TLB
恢复寄存器
图 4:进程切换与线程切换的对比。 进程切换需要切换页表和刷新 TLB,线程切换不需要。
3.3 中断上下文切换
中断上下文切换发生在中断处理时。CPU 从用户态切换到内核态,保存用户程序的上下文,执行中断处理程序,然后恢复。
中断上下文切换和进程上下文切换的区别:
- 中断上下文切换不涉及进程切换,只是从用户态到内核态的切换;
- 中断处理程序运行在内核态,使用内核栈;
- 中断处理完毕后,可能返回原进程,也可能触发调度。
3.4 模式切换
模式切换(Mode Switch)是指 CPU 从用户态切换到内核态,或从内核态切换到用户态。系统调用、中断、异常都会触发模式切换。
模式切换和上下文切换的区别:
| 对比项 | 模式切换 | 上下文切换 |
|---|---|---|
| 是否切换进程 | 不一定 | 一定 |
| 是否保存全部上下文 | 不一定 | 一定 |
| 开销 | 较小 | 较大 |
| 触发方式 | 系统调用、中断、异常 | 调度器决定 |
注意:模式切换不一定导致上下文切换。例如,系统调用进入内核后,如果不需要切换进程,处理完直接返回原进程,就没有发生上下文切换。
#mermaid-svg-klBWQxE7jihYm79l{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-klBWQxE7jihYm79l .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-klBWQxE7jihYm79l .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-klBWQxE7jihYm79l .error-icon{fill:#552222;}#mermaid-svg-klBWQxE7jihYm79l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-klBWQxE7jihYm79l .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-klBWQxE7jihYm79l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-klBWQxE7jihYm79l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-klBWQxE7jihYm79l .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-klBWQxE7jihYm79l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-klBWQxE7jihYm79l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-klBWQxE7jihYm79l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-klBWQxE7jihYm79l .marker.cross{stroke:#333333;}#mermaid-svg-klBWQxE7jihYm79l svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-klBWQxE7jihYm79l p{margin:0;}#mermaid-svg-klBWQxE7jihYm79l .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-klBWQxE7jihYm79l .cluster-label text{fill:#333;}#mermaid-svg-klBWQxE7jihYm79l .cluster-label span{color:#333;}#mermaid-svg-klBWQxE7jihYm79l .cluster-label span p{background-color:transparent;}#mermaid-svg-klBWQxE7jihYm79l .label text,#mermaid-svg-klBWQxE7jihYm79l span{fill:#333;color:#333;}#mermaid-svg-klBWQxE7jihYm79l .node rect,#mermaid-svg-klBWQxE7jihYm79l .node circle,#mermaid-svg-klBWQxE7jihYm79l .node ellipse,#mermaid-svg-klBWQxE7jihYm79l .node polygon,#mermaid-svg-klBWQxE7jihYm79l .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-klBWQxE7jihYm79l .rough-node .label text,#mermaid-svg-klBWQxE7jihYm79l .node .label text,#mermaid-svg-klBWQxE7jihYm79l .image-shape .label,#mermaid-svg-klBWQxE7jihYm79l .icon-shape .label{text-anchor:middle;}#mermaid-svg-klBWQxE7jihYm79l .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-klBWQxE7jihYm79l .rough-node .label,#mermaid-svg-klBWQxE7jihYm79l .node .label,#mermaid-svg-klBWQxE7jihYm79l .image-shape .label,#mermaid-svg-klBWQxE7jihYm79l .icon-shape .label{text-align:center;}#mermaid-svg-klBWQxE7jihYm79l .node.clickable{cursor:pointer;}#mermaid-svg-klBWQxE7jihYm79l .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-klBWQxE7jihYm79l .arrowheadPath{fill:#333333;}#mermaid-svg-klBWQxE7jihYm79l .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-klBWQxE7jihYm79l .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-klBWQxE7jihYm79l .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-klBWQxE7jihYm79l .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-klBWQxE7jihYm79l .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-klBWQxE7jihYm79l .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-klBWQxE7jihYm79l .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-klBWQxE7jihYm79l .cluster text{fill:#333;}#mermaid-svg-klBWQxE7jihYm79l .cluster span{color:#333;}#mermaid-svg-klBWQxE7jihYm79l div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-klBWQxE7jihYm79l .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-klBWQxE7jihYm79l rect.text{fill:none;stroke-width:0;}#mermaid-svg-klBWQxE7jihYm79l .icon-shape,#mermaid-svg-klBWQxE7jihYm79l .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-klBWQxE7jihYm79l .icon-shape p,#mermaid-svg-klBWQxE7jihYm79l .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-klBWQxE7jihYm79l .icon-shape .label rect,#mermaid-svg-klBWQxE7jihYm79l .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-klBWQxE7jihYm79l .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-klBWQxE7jihYm79l .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-klBWQxE7jihYm79l :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 系统调用
处理完毕, 返回原进程
需要调度
用户态
内核态
保存上下文
选择新进程
恢复上下文
图 5:模式切换与上下文切换的关系。 系统调用进入内核后,可能只发生模式切换,也可能触发上下文切换。
四、上下文切换的完整流程
上下文切换是一个复杂的过程,涉及硬件和操作系统的紧密配合。
4.1 完整流程图
进程B PCB_B PCB_A 内核 CPU 进程A 进程B PCB_B PCB_A 内核 CPU 进程A #mermaid-svg-OmAjFcUHoG0tOSwy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OmAjFcUHoG0tOSwy .error-icon{fill:#552222;}#mermaid-svg-OmAjFcUHoG0tOSwy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OmAjFcUHoG0tOSwy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OmAjFcUHoG0tOSwy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OmAjFcUHoG0tOSwy .marker.cross{stroke:#333333;}#mermaid-svg-OmAjFcUHoG0tOSwy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OmAjFcUHoG0tOSwy p{margin:0;}#mermaid-svg-OmAjFcUHoG0tOSwy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OmAjFcUHoG0tOSwy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-OmAjFcUHoG0tOSwy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-OmAjFcUHoG0tOSwy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-OmAjFcUHoG0tOSwy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-OmAjFcUHoG0tOSwy .sequenceNumber{fill:white;}#mermaid-svg-OmAjFcUHoG0tOSwy #sequencenumber{fill:#333;}#mermaid-svg-OmAjFcUHoG0tOSwy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-OmAjFcUHoG0tOSwy .messageText{fill:#333;stroke:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OmAjFcUHoG0tOSwy .labelText,#mermaid-svg-OmAjFcUHoG0tOSwy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .loopText,#mermaid-svg-OmAjFcUHoG0tOSwy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-OmAjFcUHoG0tOSwy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-OmAjFcUHoG0tOSwy .noteText,#mermaid-svg-OmAjFcUHoG0tOSwy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-OmAjFcUHoG0tOSwy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OmAjFcUHoG0tOSwy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OmAjFcUHoG0tOSwy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OmAjFcUHoG0tOSwy .actorPopupMenu{position:absolute;}#mermaid-svg-OmAjFcUHoG0tOSwy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-OmAjFcUHoG0tOSwy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OmAjFcUHoG0tOSwy .actor-man circle,#mermaid-svg-OmAjFcUHoG0tOSwy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-OmAjFcUHoG0tOSwy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 正在执行触发调度(时间片用完/阻塞)保存进程A的寄存器保存程序计数器保存栈指针保存页表基址选择进程B读取进程B的上下文恢复进程B的寄存器恢复进程B的程序计数器切换页表到进程B刷新 TLB从上次中断处继续执行
图 6:上下文切换的完整流程。 从触发调度到恢复新进程,经历保存、选择、恢复三个阶段。
4.2 详细步骤
- 触发调度:时间片用完、进程阻塞、更高优先级进程就绪等;
- 进入内核态:CPU 从用户态切换到内核态;
- 保存进程 A 的上下文:把寄存器、PC、SP、页表基址等保存到 PCB_A;
- 选择进程 B:调度器从就绪队列中选择下一个进程;
- 恢复进程 B 的上下文:从 PCB_B 中读取寄存器、PC、SP 等;
- 切换页表:把 CPU 的页表基址寄存器指向进程 B 的页表;
- 刷新 TLB:使旧的 TLB 条目失效(或使用 ASID 避免刷新);
- 返回用户态:CPU 从内核态切换回用户态;
- 继续执行进程 B:从进程 B 上次中断的地方继续执行。
4.3 一个具体的例子
假设进程 A 正在执行,时间片用完,操作系统切换到进程 B。
进程 A 的上下文:
- PC = 0x00401000(下一条指令地址);
- SP = 0x7FFF0000(栈顶);
- 通用寄存器:RAX = 10, RBX = 20, RCX = 30;
- 页表基址 = 0x1000。
进程 B 的上下文:
- PC = 0x00402000;
- SP = 0x7FFE0000;
- 通用寄存器:RAX = 100, RBX = 200, RCX = 300;
- 页表基址 = 0x2000。
切换过程:
- 保存 A 的 PC、SP、寄存器到 PCB_A;
- 从 PCB_B 读取 B 的 PC、SP、寄存器;
- 把 CPU 的页表基址寄存器改为 0x2000;
- 刷新 TLB;
- CPU 从 0x00402000 继续执行进程 B。
#mermaid-svg-9Iyush5zKqOPVb2R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9Iyush5zKqOPVb2R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9Iyush5zKqOPVb2R .error-icon{fill:#552222;}#mermaid-svg-9Iyush5zKqOPVb2R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9Iyush5zKqOPVb2R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9Iyush5zKqOPVb2R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9Iyush5zKqOPVb2R .marker.cross{stroke:#333333;}#mermaid-svg-9Iyush5zKqOPVb2R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9Iyush5zKqOPVb2R p{margin:0;}#mermaid-svg-9Iyush5zKqOPVb2R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9Iyush5zKqOPVb2R .cluster-label text{fill:#333;}#mermaid-svg-9Iyush5zKqOPVb2R .cluster-label span{color:#333;}#mermaid-svg-9Iyush5zKqOPVb2R .cluster-label span p{background-color:transparent;}#mermaid-svg-9Iyush5zKqOPVb2R .label text,#mermaid-svg-9Iyush5zKqOPVb2R span{fill:#333;color:#333;}#mermaid-svg-9Iyush5zKqOPVb2R .node rect,#mermaid-svg-9Iyush5zKqOPVb2R .node circle,#mermaid-svg-9Iyush5zKqOPVb2R .node ellipse,#mermaid-svg-9Iyush5zKqOPVb2R .node polygon,#mermaid-svg-9Iyush5zKqOPVb2R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9Iyush5zKqOPVb2R .rough-node .label text,#mermaid-svg-9Iyush5zKqOPVb2R .node .label text,#mermaid-svg-9Iyush5zKqOPVb2R .image-shape .label,#mermaid-svg-9Iyush5zKqOPVb2R .icon-shape .label{text-anchor:middle;}#mermaid-svg-9Iyush5zKqOPVb2R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9Iyush5zKqOPVb2R .rough-node .label,#mermaid-svg-9Iyush5zKqOPVb2R .node .label,#mermaid-svg-9Iyush5zKqOPVb2R .image-shape .label,#mermaid-svg-9Iyush5zKqOPVb2R .icon-shape .label{text-align:center;}#mermaid-svg-9Iyush5zKqOPVb2R .node.clickable{cursor:pointer;}#mermaid-svg-9Iyush5zKqOPVb2R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9Iyush5zKqOPVb2R .arrowheadPath{fill:#333333;}#mermaid-svg-9Iyush5zKqOPVb2R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9Iyush5zKqOPVb2R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9Iyush5zKqOPVb2R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9Iyush5zKqOPVb2R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9Iyush5zKqOPVb2R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9Iyush5zKqOPVb2R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9Iyush5zKqOPVb2R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9Iyush5zKqOPVb2R .cluster text{fill:#333;}#mermaid-svg-9Iyush5zKqOPVb2R .cluster span{color:#333;}#mermaid-svg-9Iyush5zKqOPVb2R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9Iyush5zKqOPVb2R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9Iyush5zKqOPVb2R rect.text{fill:none;stroke-width:0;}#mermaid-svg-9Iyush5zKqOPVb2R .icon-shape,#mermaid-svg-9Iyush5zKqOPVb2R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9Iyush5zKqOPVb2R .icon-shape p,#mermaid-svg-9Iyush5zKqOPVb2R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9Iyush5zKqOPVb2R .icon-shape .label rect,#mermaid-svg-9Iyush5zKqOPVb2R .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9Iyush5zKqOPVb2R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9Iyush5zKqOPVb2R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9Iyush5zKqOPVb2R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进程B
进程A
保存
恢复
PC=0x401000
SP=0x7FFF0000
页表=0x1000
PC=0x402000
SP=0x7FFE0000
页表=0x2000
内核
图 7:上下文切换的具体例子。 从进程 A 的上下文切换到进程 B 的上下文。
五、上下文切换的开销
上下文切换不是免费的。它的开销来自多个方面。
5.1 直接开销
直接开销是上下文切换本身消耗的时间:
| 开销来源 | 说明 |
|---|---|
| 保存寄存器 | 把寄存器写入 PCB |
| 恢复寄存器 | 从 PCB 读取寄存器 |
| 切换页表 | 修改页表基址寄存器 |
| 刷新 TLB | 使旧 TLB 条目失效 |
| 内核代码执行 | 调度器选择进程 |
5.2 间接开销
间接开销是上下文切换对系统性能的间接影响:
| 开销来源 | 说明 |
|---|---|
| CPU 缓存失效 | 新进程的代码和数据可能不在缓存中 |
| TLB 失效 | 地址转换需要重新查询页表 |
| 分支预测失效 | CPU 的分支预测器需要重新学习 |
| 流水线冲刷 | CPU 流水线被清空 |
#mermaid-svg-uUy1nGm5qaN7NBqd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uUy1nGm5qaN7NBqd .error-icon{fill:#552222;}#mermaid-svg-uUy1nGm5qaN7NBqd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uUy1nGm5qaN7NBqd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uUy1nGm5qaN7NBqd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uUy1nGm5qaN7NBqd .marker.cross{stroke:#333333;}#mermaid-svg-uUy1nGm5qaN7NBqd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uUy1nGm5qaN7NBqd p{margin:0;}#mermaid-svg-uUy1nGm5qaN7NBqd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster-label text{fill:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster-label span{color:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster-label span p{background-color:transparent;}#mermaid-svg-uUy1nGm5qaN7NBqd .label text,#mermaid-svg-uUy1nGm5qaN7NBqd span{fill:#333;color:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd .node rect,#mermaid-svg-uUy1nGm5qaN7NBqd .node circle,#mermaid-svg-uUy1nGm5qaN7NBqd .node ellipse,#mermaid-svg-uUy1nGm5qaN7NBqd .node polygon,#mermaid-svg-uUy1nGm5qaN7NBqd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uUy1nGm5qaN7NBqd .rough-node .label text,#mermaid-svg-uUy1nGm5qaN7NBqd .node .label text,#mermaid-svg-uUy1nGm5qaN7NBqd .image-shape .label,#mermaid-svg-uUy1nGm5qaN7NBqd .icon-shape .label{text-anchor:middle;}#mermaid-svg-uUy1nGm5qaN7NBqd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uUy1nGm5qaN7NBqd .rough-node .label,#mermaid-svg-uUy1nGm5qaN7NBqd .node .label,#mermaid-svg-uUy1nGm5qaN7NBqd .image-shape .label,#mermaid-svg-uUy1nGm5qaN7NBqd .icon-shape .label{text-align:center;}#mermaid-svg-uUy1nGm5qaN7NBqd .node.clickable{cursor:pointer;}#mermaid-svg-uUy1nGm5qaN7NBqd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uUy1nGm5qaN7NBqd .arrowheadPath{fill:#333333;}#mermaid-svg-uUy1nGm5qaN7NBqd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uUy1nGm5qaN7NBqd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uUy1nGm5qaN7NBqd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uUy1nGm5qaN7NBqd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uUy1nGm5qaN7NBqd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uUy1nGm5qaN7NBqd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster text{fill:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd .cluster span{color:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uUy1nGm5qaN7NBqd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uUy1nGm5qaN7NBqd rect.text{fill:none;stroke-width:0;}#mermaid-svg-uUy1nGm5qaN7NBqd .icon-shape,#mermaid-svg-uUy1nGm5qaN7NBqd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uUy1nGm5qaN7NBqd .icon-shape p,#mermaid-svg-uUy1nGm5qaN7NBqd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uUy1nGm5qaN7NBqd .icon-shape .label rect,#mermaid-svg-uUy1nGm5qaN7NBqd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uUy1nGm5qaN7NBqd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uUy1nGm5qaN7NBqd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uUy1nGm5qaN7NBqd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文切换开销
直接开销
间接开销
保存/恢复寄存器
切换页表
刷新 TLB
CPU 缓存失效
TLB 失效
分支预测失效
图 8:上下文切换的开销来源。 直接开销和间接开销共同影响性能。
5.3 开销有多大?
上下文切换的开销因硬件和操作系统而异,通常在 几微秒 左右。听起来不多,但如果系统每秒切换上千次,累积起来就很可观。
假设一次上下文切换需要 5 微秒,每秒切换 1000 次,那么每秒有 5 毫秒花在切换上,占 CPU 时间的 0.5%。如果每秒切换 10000 次,就有 5% 的 CPU 时间花在切换上。
5.4 如何测量上下文切换次数?
在 Linux 中,可以使用以下命令查看上下文切换次数:
bash
# 查看系统整体的上下文切换统计
vmstat 1
# 查看特定进程的上下文切换
pidstat -w -p PID 1
vmstat 输出中的 cs 列表示每秒上下文切换次数。pidstat 可以查看特定进程的自愿和非自愿上下文切换。
六、上下文切换的触发时机
上下文切换不是随时发生的,而是在特定时机触发。
6.1 触发时机
| 触发时机 | 说明 | 是否自愿 |
|---|---|---|
| 时间片用完 | 分时系统中,进程用完时间片 | 非自愿 |
| 进程阻塞 | 进程请求 I/O 或等待事件 | 自愿 |
| 进程终止 | 进程执行完毕 | 自愿 |
| 更高优先级进程就绪 | 抢占式调度 | 非自愿 |
| 系统调用 | 进程主动让出 CPU | 自愿 |
| 中断处理 | 中断处理完后可能触发调度 | 非自愿 |
6.2 自愿切换与非自愿切换
自愿上下文切换 (Voluntary Context Switch):进程主动放弃 CPU,如等待 I/O、调用 sleep()、等待锁。
非自愿上下文切换(Involuntary Context Switch):进程被迫放弃 CPU,如时间片用完、被更高优先级进程抢占。
在 Linux 中,可以用 pidstat -w 查看这两种切换:
bash
pidstat -w -p PID 1
输出中的 cswch/s 是自愿切换,nvcswch/s 是非自愿切换。
#mermaid-svg-cKUlSGERzv5OFidy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cKUlSGERzv5OFidy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cKUlSGERzv5OFidy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cKUlSGERzv5OFidy .error-icon{fill:#552222;}#mermaid-svg-cKUlSGERzv5OFidy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cKUlSGERzv5OFidy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cKUlSGERzv5OFidy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cKUlSGERzv5OFidy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cKUlSGERzv5OFidy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cKUlSGERzv5OFidy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cKUlSGERzv5OFidy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cKUlSGERzv5OFidy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cKUlSGERzv5OFidy .marker.cross{stroke:#333333;}#mermaid-svg-cKUlSGERzv5OFidy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cKUlSGERzv5OFidy p{margin:0;}#mermaid-svg-cKUlSGERzv5OFidy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cKUlSGERzv5OFidy .cluster-label text{fill:#333;}#mermaid-svg-cKUlSGERzv5OFidy .cluster-label span{color:#333;}#mermaid-svg-cKUlSGERzv5OFidy .cluster-label span p{background-color:transparent;}#mermaid-svg-cKUlSGERzv5OFidy .label text,#mermaid-svg-cKUlSGERzv5OFidy span{fill:#333;color:#333;}#mermaid-svg-cKUlSGERzv5OFidy .node rect,#mermaid-svg-cKUlSGERzv5OFidy .node circle,#mermaid-svg-cKUlSGERzv5OFidy .node ellipse,#mermaid-svg-cKUlSGERzv5OFidy .node polygon,#mermaid-svg-cKUlSGERzv5OFidy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cKUlSGERzv5OFidy .rough-node .label text,#mermaid-svg-cKUlSGERzv5OFidy .node .label text,#mermaid-svg-cKUlSGERzv5OFidy .image-shape .label,#mermaid-svg-cKUlSGERzv5OFidy .icon-shape .label{text-anchor:middle;}#mermaid-svg-cKUlSGERzv5OFidy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cKUlSGERzv5OFidy .rough-node .label,#mermaid-svg-cKUlSGERzv5OFidy .node .label,#mermaid-svg-cKUlSGERzv5OFidy .image-shape .label,#mermaid-svg-cKUlSGERzv5OFidy .icon-shape .label{text-align:center;}#mermaid-svg-cKUlSGERzv5OFidy .node.clickable{cursor:pointer;}#mermaid-svg-cKUlSGERzv5OFidy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cKUlSGERzv5OFidy .arrowheadPath{fill:#333333;}#mermaid-svg-cKUlSGERzv5OFidy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cKUlSGERzv5OFidy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cKUlSGERzv5OFidy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cKUlSGERzv5OFidy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cKUlSGERzv5OFidy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cKUlSGERzv5OFidy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cKUlSGERzv5OFidy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cKUlSGERzv5OFidy .cluster text{fill:#333;}#mermaid-svg-cKUlSGERzv5OFidy .cluster span{color:#333;}#mermaid-svg-cKUlSGERzv5OFidy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cKUlSGERzv5OFidy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cKUlSGERzv5OFidy rect.text{fill:none;stroke-width:0;}#mermaid-svg-cKUlSGERzv5OFidy .icon-shape,#mermaid-svg-cKUlSGERzv5OFidy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cKUlSGERzv5OFidy .icon-shape p,#mermaid-svg-cKUlSGERzv5OFidy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cKUlSGERzv5OFidy .icon-shape .label rect,#mermaid-svg-cKUlSGERzv5OFidy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cKUlSGERzv5OFidy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cKUlSGERzv5OFidy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cKUlSGERzv5OFidy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文切换触发时机
自愿切换
非自愿切换
等待 I/O
调用 sleep
等待锁
时间片用完
被抢占
中断触发调度
图 9:自愿切换与非自愿切换。 自愿切换是进程主动放弃 CPU,非自愿切换是被迫放弃。
七、如何减少上下文切换
上下文切换是必要的,但过多的切换会降低系统性能。以下是一些减少上下文切换的方法。
7.1 调整时间片大小
时间片太小,切换频繁;时间片太大,响应变差。需要根据系统负载调整时间片。
7.2 使用线程而非进程
线程切换比进程切换开销小得多。如果任务之间需要共享数据,使用多线程比多进程更高效。
7.3 减少锁竞争
锁竞争会导致线程频繁阻塞和唤醒,增加上下文切换。可以通过减小锁粒度、使用无锁数据结构等方法减少锁竞争。
7.4 使用 I/O 多路复用
select、poll、epoll 等 I/O 多路复用技术可以让一个线程管理多个 I/O 连接,减少线程数量和上下文切换。
7.5 使用协程
协程是用户态的轻量级线程,切换在用户态完成,不需要进入内核,开销远小于线程切换。
#mermaid-svg-5eBfMyVD64zOuWaX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5eBfMyVD64zOuWaX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5eBfMyVD64zOuWaX .error-icon{fill:#552222;}#mermaid-svg-5eBfMyVD64zOuWaX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5eBfMyVD64zOuWaX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5eBfMyVD64zOuWaX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5eBfMyVD64zOuWaX .marker.cross{stroke:#333333;}#mermaid-svg-5eBfMyVD64zOuWaX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5eBfMyVD64zOuWaX p{margin:0;}#mermaid-svg-5eBfMyVD64zOuWaX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5eBfMyVD64zOuWaX .cluster-label text{fill:#333;}#mermaid-svg-5eBfMyVD64zOuWaX .cluster-label span{color:#333;}#mermaid-svg-5eBfMyVD64zOuWaX .cluster-label span p{background-color:transparent;}#mermaid-svg-5eBfMyVD64zOuWaX .label text,#mermaid-svg-5eBfMyVD64zOuWaX span{fill:#333;color:#333;}#mermaid-svg-5eBfMyVD64zOuWaX .node rect,#mermaid-svg-5eBfMyVD64zOuWaX .node circle,#mermaid-svg-5eBfMyVD64zOuWaX .node ellipse,#mermaid-svg-5eBfMyVD64zOuWaX .node polygon,#mermaid-svg-5eBfMyVD64zOuWaX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5eBfMyVD64zOuWaX .rough-node .label text,#mermaid-svg-5eBfMyVD64zOuWaX .node .label text,#mermaid-svg-5eBfMyVD64zOuWaX .image-shape .label,#mermaid-svg-5eBfMyVD64zOuWaX .icon-shape .label{text-anchor:middle;}#mermaid-svg-5eBfMyVD64zOuWaX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5eBfMyVD64zOuWaX .rough-node .label,#mermaid-svg-5eBfMyVD64zOuWaX .node .label,#mermaid-svg-5eBfMyVD64zOuWaX .image-shape .label,#mermaid-svg-5eBfMyVD64zOuWaX .icon-shape .label{text-align:center;}#mermaid-svg-5eBfMyVD64zOuWaX .node.clickable{cursor:pointer;}#mermaid-svg-5eBfMyVD64zOuWaX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5eBfMyVD64zOuWaX .arrowheadPath{fill:#333333;}#mermaid-svg-5eBfMyVD64zOuWaX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5eBfMyVD64zOuWaX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5eBfMyVD64zOuWaX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5eBfMyVD64zOuWaX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5eBfMyVD64zOuWaX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5eBfMyVD64zOuWaX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5eBfMyVD64zOuWaX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5eBfMyVD64zOuWaX .cluster text{fill:#333;}#mermaid-svg-5eBfMyVD64zOuWaX .cluster span{color:#333;}#mermaid-svg-5eBfMyVD64zOuWaX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5eBfMyVD64zOuWaX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5eBfMyVD64zOuWaX rect.text{fill:none;stroke-width:0;}#mermaid-svg-5eBfMyVD64zOuWaX .icon-shape,#mermaid-svg-5eBfMyVD64zOuWaX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5eBfMyVD64zOuWaX .icon-shape p,#mermaid-svg-5eBfMyVD64zOuWaX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5eBfMyVD64zOuWaX .icon-shape .label rect,#mermaid-svg-5eBfMyVD64zOuWaX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5eBfMyVD64zOuWaX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5eBfMyVD64zOuWaX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5eBfMyVD64zOuWaX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 减少上下文切换
调整时间片
使用线程而非进程
减少锁竞争
I/O 多路复用
使用协程
图 10:减少上下文切换的方法。
八、上下文切换的实际应用
8.1 Linux 中的上下文切换
Linux 使用 switch_to 宏完成进程上下文切换。它保存当前进程的寄存器,恢复下一个进程的寄存器,并切换栈和页表。
8.2 容器与上下文切换
容器共享宿主机内核,容器内的进程切换仍然是普通的进程切换。但容器启动比虚拟机快,因为不需要启动完整的内核。
8.3 高性能计算中的上下文切换
在高性能计算中,上下文切换是性能杀手。例如,高频交易系统要求极低的延迟,通常会:
- 使用 CPU 亲和性,把进程绑定到固定 CPU;
- 使用轮询而非中断,避免中断上下文切换;
- 使用用户态网络协议栈,绕过内核。
#mermaid-svg-R3C5j9qdvjCJyE2J{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-R3C5j9qdvjCJyE2J .error-icon{fill:#552222;}#mermaid-svg-R3C5j9qdvjCJyE2J .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R3C5j9qdvjCJyE2J .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R3C5j9qdvjCJyE2J .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R3C5j9qdvjCJyE2J .marker.cross{stroke:#333333;}#mermaid-svg-R3C5j9qdvjCJyE2J svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R3C5j9qdvjCJyE2J p{margin:0;}#mermaid-svg-R3C5j9qdvjCJyE2J .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster-label text{fill:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster-label span{color:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster-label span p{background-color:transparent;}#mermaid-svg-R3C5j9qdvjCJyE2J .label text,#mermaid-svg-R3C5j9qdvjCJyE2J span{fill:#333;color:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J .node rect,#mermaid-svg-R3C5j9qdvjCJyE2J .node circle,#mermaid-svg-R3C5j9qdvjCJyE2J .node ellipse,#mermaid-svg-R3C5j9qdvjCJyE2J .node polygon,#mermaid-svg-R3C5j9qdvjCJyE2J .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R3C5j9qdvjCJyE2J .rough-node .label text,#mermaid-svg-R3C5j9qdvjCJyE2J .node .label text,#mermaid-svg-R3C5j9qdvjCJyE2J .image-shape .label,#mermaid-svg-R3C5j9qdvjCJyE2J .icon-shape .label{text-anchor:middle;}#mermaid-svg-R3C5j9qdvjCJyE2J .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-R3C5j9qdvjCJyE2J .rough-node .label,#mermaid-svg-R3C5j9qdvjCJyE2J .node .label,#mermaid-svg-R3C5j9qdvjCJyE2J .image-shape .label,#mermaid-svg-R3C5j9qdvjCJyE2J .icon-shape .label{text-align:center;}#mermaid-svg-R3C5j9qdvjCJyE2J .node.clickable{cursor:pointer;}#mermaid-svg-R3C5j9qdvjCJyE2J .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-R3C5j9qdvjCJyE2J .arrowheadPath{fill:#333333;}#mermaid-svg-R3C5j9qdvjCJyE2J .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R3C5j9qdvjCJyE2J .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R3C5j9qdvjCJyE2J .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3C5j9qdvjCJyE2J .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-R3C5j9qdvjCJyE2J .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3C5j9qdvjCJyE2J .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster text{fill:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J .cluster span{color:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R3C5j9qdvjCJyE2J .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-R3C5j9qdvjCJyE2J rect.text{fill:none;stroke-width:0;}#mermaid-svg-R3C5j9qdvjCJyE2J .icon-shape,#mermaid-svg-R3C5j9qdvjCJyE2J .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R3C5j9qdvjCJyE2J .icon-shape p,#mermaid-svg-R3C5j9qdvjCJyE2J .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-R3C5j9qdvjCJyE2J .icon-shape .label rect,#mermaid-svg-R3C5j9qdvjCJyE2J .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R3C5j9qdvjCJyE2J .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-R3C5j9qdvjCJyE2J .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-R3C5j9qdvjCJyE2J :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文切换应用
Linux: switch_to 宏
容器: 普通进程切换
高性能计算: 减少切换
图 11:上下文切换的实际应用。
九、常见误区澄清
| 误区 | 真相 |
|---|---|
| "上下文切换就是模式切换" | 模式切换是用户态到内核态,上下文切换是进程切换,两者不同 |
| "线程切换和进程切换开销一样" | 线程切换不需要切换页表,开销小得多 |
| "上下文切换没有开销" | 上下文切换涉及保存恢复寄存器、切换页表、刷新 TLB,开销较大 |
| "系统调用一定会导致上下文切换" | 不一定,如果不需要切换进程,只发生模式切换 |
| "上下文切换越多越好" | 过多切换会降低性能,需要平衡 |
| "所有操作系统的上下文切换机制相同" | Linux、Windows 的实现细节不同,但核心思想一致 |
| "上下文切换只发生在进程之间" | 线程之间、中断处理也会发生上下文切换 |