✏️ update

This commit is contained in:
nick
2020-01-21 11:26:58 +08:00
parent 370cd588bc
commit b0367180db
5 changed files with 45 additions and 373 deletions

View File

@ -1,6 +1,6 @@
## 什么是 CPU 上下文
Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行。当然,这些任务实际上并不是真的在同时运行,而是因为系统在很短的时间内,将 CPU 轮流分配给它们造成多任务同时运行的错觉。而在每个任务运行前CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(Program CounterPC)
Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行。当然,这些任务实际上并不是真的在同时运行,而是因为系统在很短的时间内,将 CPU 轮流分配给它们造成多任务同时运行的错觉。而在每个任务运行前CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好 CPU 寄存器和程序计数器Program CounterPC
CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU 上下文。
@ -10,7 +10,7 @@ CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,
## CPU 上下文切换分类
CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置, 运行新任务。
CPU 上下文切换,就是先把前一个任务的 CPU 上下文也就是 CPU 寄存器和程序计数器保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。
根据任务的不同CPU 的上下文切换就可以分为 __进程上下文切换__、 __线程上下文切换__ 以及 __中断上下文切换__
@ -18,8 +18,8 @@ CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU
Linux 按照特权等级把进程的运行空间分为内核空间和用户空间CPU 特权等级的 Ring 0 和 Ring 3。
内核空间(Ring 0)具有最高权限,可以直接访问所有资源;
用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统 调用陷入到内核中,才能访问这些特权资源。
内核空间Ring 0)具有最高权限,可以直接访问所有资源;
用户空间Ring 3只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统 调用陷入到内核中,才能访问这些特权资源。
进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。
@ -29,18 +29,18 @@ Linux 按照特权等级,把进程的运行空间分为内核空间和用户
CPU 寄存器里原来用户态的指令位置需要先保存起来。接着为了执行内核态代码CPU 寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。
而系统调用结束后CPU 寄存器需要 __恢复__原来保存的用户态然后再切换到用户空间继续运行进程。所以 __一次系统调用的过程其实是发生了两次 CPU 上下文切换__。
而系统调用结束后CPU 寄存器需要__恢复__原来保存的用户态然后再切换到用户空间继续运行进程。所以 __一次系统调用的过程其实是发生了两次 CPU 上下文切换__。
需要注意的是,系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也
不会切换进程。这跟我们通常所说的进程上下文切换是不一样的: __进程上下文切换是指从一个进程切换到另一个进程运行。而系统调用过程中一直是同一个进程在运行__ 。所以__系统调用过程通常称为特权模式切换而不是上下文切换__。但实际上系统调用过程中CPU 的上下文切换还是无法避免的。
#### 进程在什么时候才会被调度到 CPU 上运行
最容易想到的一个时机,就是进程执行完终止了,它之前使用的 CPU 会释放出来,这个时候再从就绪队列里,拿一个新的进程过来运行。其实还有很多其他场景,也会触发进程调度
最容易想到的一个时机,就是进程执行完终止了,它之前使用的 CPU 会释放出来,这个时候再从就绪队列里,拿一个新的进程过来运行。其实还有很多其他场景,也会触发进程调度
其一为了保证所有进程可以得到公平调度CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行。
其二,进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时
其二,进程在系统资源不足比如内存不足时,要等到资源满足后才可以运行,这个时
候进程也会被挂起,并由系统调度其他进程运行。
其三,当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度。
@ -59,7 +59,7 @@ __线程与进程最大的区别在于线程是调度的基本单位而进
所以对于线程和进程我们可以这么理解__当进程只有一个线程时可以认为进程就等于线程__。当进程拥有多个线程时这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。另外线程也有自己的私有数据比如栈和寄存器等这些在上下文换时也是需要保存的。
线程的上下文切换其实就可以分为两种情况:
线程的上下文切换其实就可以分为两种情况
第一种, 前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样。
@ -80,7 +80,7 @@ __线程与进程最大的区别在于线程是调度的基本单位而进
vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分 析 CPU 上下文切换和中断的次数。
下面就是一个 vmstat 的使用示例:
下面就是一个 vmstat 的使用示例
```
vmstat 5 #每隔 5 秒输出 1 组数据
@ -94,19 +94,19 @@ procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
结果含义说明
- cs(context switch)是每秒上下文切换的次数。
- cscontext switch是每秒上下文切换的次数。
- in(interrupt)则是每秒中断的次数。
- ininterrupt则是每秒中断的次数。
- r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
- rRunning or Runnable是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
- b(Blocked)则是处于不可中断睡眠状态的进程数。
- bBlocked则是处于不可中断睡眠状态的进程数。
可以看到,这个例子中的上下文切换次数 cs 是 8 次,而系统中断次数 in 则是 2 次,而 就绪队列长度 r 和不可中断状态进程数 b 都是 0。
vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,你就可以查看每个进程上下文切换的情况了。
示例:
示例
```
pidstat -w 5 # 每隔 5 秒输出 1 组数据
@ -123,7 +123,7 @@ Linux 4.4.0-142-generic (10-53-166-171) 07/07/2019 _x86_64_ (2 CPU)
04:05:58 PM 0 10 0.20 0.00 watchdog/0
```
这个结果中有两列内容是我们的重点关注对象。一个是 cswch ,表示每秒自愿上下文切换 (voluntary context switches)的次数,另一个则是 nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。
这个结果中有两列内容是我们的重点关注对象。一个是 cswch ,表示每秒自愿上下文切换 voluntary context switches的次数,另一个则是 nvcswch ,表示每秒非自愿上下文切换non voluntary context switches的次数。
这两个概念一定要牢牢记住,因为它们意味着不同的性能问题: