# CPU 上下文切换

对于多任务操作系统，支持远大于CPU数量的任务同时运行。操作系统通过给不同的任务划分对应时间片，在用户侧看来，多个任务并发运行。而在每一个任务运行前，cpu都需要知道任务从哪里加载、又从哪里开始运行，需要操作系统预先设置好**cpu寄存器以及程序计数器**。

## 1. 什么是cpu上下文

CPU 寄存器和程序计数器就是 CPU 上下文，因为它们都是 CPU 在运行任何任务前，必须的依赖环境。

* CPU 寄存器是 CPU 内置的容量小、但速度极快的存储部件，可以用来暂存数据、指令、地址；在cpu的控制部件中有指令寄存器（IR）、程序计数器（PC），cpu 的算术以及逻辑部件中包含累加器（ACC）。
* 程序计数器则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。

## 2. 什么是上下文切换

把上一个任务的CPU上下文保存下来，然后加载下一个任务的cpu上下文，跳转到新任务的程序计数器，执行新的任务。保存下来的cpu上下文会保存在内核中。在任务重新调度执行时再次加载，保证原任务不受影响。

## 3. 上下文切换类型

* 系统调用的上下文切换
* 进程上下文切换
* 线程上下文切换
* 中断上下文切换

### 3.1 系统调用的上下文切换

Linux 根据特权等，将运行空间划分为内核空间以及用户空间。内核空间具有最高权限，可以管理所有资源，用户空间只能管理受限资源，不能直接访问内存资源，必须通过系统调用陷入内核才能访问特定资源。

![kernel-user-ring](https://pic2.zhimg.com/80/v2-d831951a5e41bbfb1e0e1151a8a2b649_720w.jpg)

由系统调用陷入内核完成从用户态到内核态的转变，这个过程发生的上下文切换：

![trap-context](https://pic3.zhimg.com/80/v2-440bb1699b2fa0f0340b38eabcbd7452_720w.jpg)

cpu寄存器的用户态指令位置需要先保存下来，然后加载内核态指令位置，最后跳转到内核态运行任务。系统调用结束后，需要重新回到用户态，恢复用户态上下文。一次系统调用发生两次上下文切换。不过，需要注意的是，系统调用过程中，并不会涉及到虚拟内存等进程用户态的资源，也不会切换进程。这跟我们通常所说的进程上下文切换是不一样的（系统调用在同一个进程里运行）。 所以系统调用过程通常称为 特权模式切换 ，而不是上下文切换。但实际上，系统调用过程中，CPU 的上下文切换还是无法避免的。

### 3.1 进程上下文切换

首先进程是由内核管理以及调度的，进程的切换只能发生在内核态。所以进程的上下文切换不仅包括了虚拟内存，栈，全局变量等用户空间的资源，也包括了内核堆栈，寄存器等内核空间的状态。**因此进程间上下文切换比系统调用多了一步。**&#x5728;保存当前进程的内核状态以及CPU寄存器之后，需要保存该进程用户空间的虚拟内存，栈等保存下来；在恢复下一个进程的内核态后，需要恢复下一个进程的虚拟内存以及栈。

**每次上下文切换需要几十纳秒到数微秒之间。** 这个时间还是相当客观的，在高并发场景中，进程下上文切换次数较多，很容易出现大量CPU时间浪费在寄存器，内核栈以及虚拟内存等资源的保存以及恢复上，从而大大减少了进程运行的实际时间。这也是导致系统平均负载升高的原因之一。

**进程上下文切换的时机**：

* 进程所分配的时间片耗尽，被系统挂起
* 系统资源不足时，需要等待资源满足才能运行
* 进程调用sleep等主动挂起
* 优先级抢占，更高优先级进程运行，当前进程挂起
* 发生硬件中断时，cpu上的进程被挂起，运行当前中断服务

### 3.2 线程上下文切换

线程与进程的一大区别在于，**线程是调度的基本单位，进程是资源拥有的基本单位**。内核任务的调度对象是线程，而进程只是给线程提供了虚拟内存、全局变量等资源。线程也拥有自己的数据，比如栈以及寄存器，在上下文切换是也需要保存。线程上下文切换分为两种：

* 不同进程的两个线程，资源不共享，实际上就是进程的上下文切换。
* 同一进程的两个线程，共享虚拟内存，切换时，只需要切换线程的私有数据、寄存器等。

虽然同为上下文切换，但同进程内的线程切换，要比多进程间的切换消耗更少的资源，这也正是多线程代替多进程的一个优势。

### 3.3 中断上下文切换

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

**与进程上下文切换不同，中断上下文切换不涉及用户态的操作。**&#x6240;以即使一个中断打断了一个正在用户态处理的进程，陷入内核，也不需要保存和恢复进程的虚拟内存、用户栈等。中断上下文只包括内核态发中断服务所需要的状态：包括cpu寄存器、内核堆栈、硬件中断参数等。在中断程序处理结束后，恢复之前进程的内核态上下文，因为用户态的上下文没有被改变，重置PC后可以继续执行。

对同一个 CPU 来说， **中断处理比进程拥有更高的优先级** ，所以中断上下文切换并不会与进程上下文切换同时发生。同样道理，由于中断会打断正常进程的调度和执行，所以大部分中断处理程序都短小精悍，以便尽可能快的执行结束。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://books.innohub.top/os/info/05-context-exchange.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
