Missmiaom
3/2/2020 - 3:34 AM

context switch

上下文切换

系统调用

进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。从用户态到内核态的转变,需要通过系统调用来完成。

系统调用中的上下文切换

系统调用过程一直是同一个进程运行,上下文切换只改变了CPU寄存器的值,用于指向指令地址,并不会涉及到虚拟内存等进程用户态的资源的切换

进程上下文切换

进程上下文切换内容

进程是由内核来管理和调度的,进程的切换只能发生在内核态

进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、CPU寄存器等内核空间的状态。

因此,进程的上下文切换就比系统调用时多了一步:在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、用户栈等保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

进程上下文切换的影响

  1. 每次上下文切换都需要几十纳秒到数微秒的 CPU 时间。在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间。这也是导致平均负载升高的一个重要因素。
  2. Linux 通过 TLB(Translation Lookaside Buffer)来管理虚拟内存到物理内存的映射关系。当虚拟内存更新后,TLB 也需要刷新,内存的访问也会随之变慢。特别是在多处理器系统上,缓存是被多个处理器共享的,刷新缓存不仅会影响当前处理器的进程,还会影响共享缓存的其他处理器的进程

进程切换时机

Linux 为每个 CPU 都维护了一个就绪队列,将活跃进程(即正在运行和正在等待 CPU 的进程)按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。

  1. 时间片耗尽:为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行。
  2. 资源不足:进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行。
  3. 主动挂起:当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度。
  4. 优先级更高:当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行。
  5. 硬件中断:发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。

线程上下文切换

同一进程的不同线程切换时只改变了线程的私有数据,内核堆栈和CPU寄存器的值

中断上下文切换

跟进程上下文不同,中断上下文切换并不涉及到进程的用户态。

所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。

中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等

上下文切换量化指标

上下文切换观测指标

# 进程上下文切换情况
cat /proc/<pid>/status | grep ctxt
# 每5秒输出线程上下文切换情况
pidstat -wt 5
  • cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数。是指进程无法获取所需资源,导致的上下文切换。
  • nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换

上下文切换在数百到一万次每秒以内是正常情况,但如果超过一万,则说明出现了性能问题:

  • 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题。
  • 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈。
  • 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。

进程上下文切换时间测量

方案一:系统调用(失败)

方法:通过最简单的系统调用 gettid 来计算进程上下文的切换时间。

结果:失败,系统调用并不是一次完整的进程上下文切换,运行期间,vmstat 所显示的上下文切换次数并没有明显的增长。

代码timesyscall.c

方案二:futex

方法:通过 futex 来进行进程之间的切换。具体做法是父子进程轮流 wait 并 wake up 对方。

结果: 上下文切换时间中包含 futex 系统调用的时间,并且因为其搅乱了CPU Caches(L1,L2,L3,TLB)所以结果会比较大。

代码timectxsw.c

参考

  1. futex wiki
  2. futex man
  3. futex are tricky

总结

  1. 上下文切换非常昂贵,大约需要花费30us的CPU开销
  2. CPU Cache 失效会导致上下文切换时间增长
  3. 虚拟化会导致上下文切换时间增长

Ref

https://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html