巢湖路桥建设集团有限公司网站,saas系统架构,php网站开发指导教材 文献,比百度更好的网站Linux 性能实战 | 第 8 篇 上下文切换、内核线程与调度延迟 #x1f422; #x1f517; 上下文切换#xff1a;CPU 的“换挡”开销 在上一章中#xff0c;我们探讨了调度器如何通过进程迁移来实现 CPU 负载均衡。我们提到了迁移是有成本的#xff0c;这个成本的核心就是 上…Linux 性能实战 | 第 8 篇 上下文切换、内核线程与调度延迟 上下文切换CPU 的“换挡”开销在上一章中我们探讨了调度器如何通过进程迁移来实现 CPU 负载均衡。我们提到了迁移是有成本的这个成本的核心就是上下文切换 (Context Switch)。想象一下你正在专注地写一份复杂的代码执行一个进程这时突然来了一个紧急电话中断你不得不放下手头的工作记录下当前的思路保存现场然后去处理电话切换到内核态处理中断。打完电话后你还需要回忆刚才写到哪里了恢复现场才能继续编程。这个“放下-切换-恢复”的过程就是一次上下文切换。对 CPU 而言上下文切换意味着保存当前进程/线程的“快照”包括 CPU 寄存器程序计数器、栈指针等、进程状态、内存管理信息等。加载下一个进程/线程的“快照”到 CPU 寄存器中。刷新 TLB (Translation Lookaside Buffer)导致虚拟地址到物理地址的缓存失效。CPU 缓存失效新进程的代码和数据很可能不在 CPU 的 L1/L2/L3 缓存中需要从更慢的内存中重新加载。一句话总结上下文切换是必要的“恶”。没有它多任务系统无法工作。但过于频繁的切换就像一个员工不停地在多个任务间来回切换大部分时间都浪费在了“切换”本身而不是真正地“执行”任务导致系统整体效率大幅下降。 什么时候会发生上下文切换上下文切换主要分为三类1. 进程上下文切换 (Process Context Switch)这是开销最大的一种切换。它不仅涉及到内核态和用户态的切换还涉及到虚拟内存空间的切换。时间片用完CFS 调度器为了“公平”会中断当前进程让给vruntime更小的进程。进程阻塞当进程需要等待某个资源时如等待磁盘 I/O、等待网络数据、或者通过sleep主动挂起它会被置为INTERRUPTIBLE或UNINTERRUPTIBLE状态从而触发调度让出 CPU。更高优先级的进程就绪一个实时进程如SCHED_RR变为可运行状态会立即抢占当前正在运行的普通进程。2. 线程上下文切换 (Thread Context Switch)当同一进程内的两个线程之间发生切换时因为它们共享同一个虚拟内存空间所以切换时不需要更换页表TLB 也不会被完全刷新。因此它的开销比进程上下文切换要小得多。3. 中断上下文切换 (Interrupt Context Switch)为了快速响应硬件事件如网卡收到数据包、硬盘完成读写CPU 会暂停当前运行的进程切换到内核态去执行一个中断服务程序 (Interrupt Service Routine, ISR)。中断上下文的切换非常快因为它只涉及少量内核信息的保存和恢复不涉及用户进程。但它会打断正常进程的执行如果中断过于频繁也会严重影响系统性能。️ 实战定位上下文切换元凶场景一个高并发的 Web 服务器在压力测试期间top显示系统 CPU 使用率并不高例如ussy只有 30%但load average却异常地高并且服务响应延迟RT剧增。1. 初步诊断vmstatvmstat是快速发现上下文切换问题的利器。# 每秒输出一次报告vmstat1输出procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 123456 10240 567890 0 0 0 20 1024 200000 15 15 70 0 0 2 0 0 123450 10240 567892 0 0 0 30 1280 250000 18 17 65 0 0 1 0 0 123440 10240 567898 0 0 0 25 1150 220000 16 16 68 0 0关注两列cs(context switch)每秒上下文切换的次数。这个值通常在几千到几万是正常的。但如果飙升到几十万甚至上百万就表明系统存在严重的调度问题。in(interrupt)每秒中断的次数。在这个案例中cs高达 20 多万显然是问题的根源。CPU 的大部分时间都花在了“换挡”上而不是在执行应用代码us或内核代码sy。2. 深入分析pidstat找到了问题下一步就是定位是哪个进程导致的。pidstat是sysstat工具包中的一员专门用于分析进程级别的统计信息。# -w: 显示上下文切换信息# -p ALL: 监控所有进程# 1: 每秒输出一次pidstat -w -p ALL1输出Linux 5.4.0-100-generic (hostname) 01/31/26 _x86_64_ (8 CPU) 10:30:01 UID PID cswch/s nvcswch/s Command 10:30:02 1000 12345 150000.00 5.00 nginx 10:30:02 0 54321 200.00 10.00 kworker/u16:0 10:30:02 1001 9876 10.00 0.00 redis-server ...关注两列cswch/s(voluntary context switches)自愿上下文切换。通常是因为进程等待资源如 I/O而主动放弃 CPU。如果这个值很高说明应用可能存在大量的 I/O 等待或者同步锁竞争。nvcswch/s(non-voluntary context switches)非自愿上下文切换。通常是因为时间片用完或者被更高优先级的进程抢占。如果这个值很高说明 CPU 正在被多个活跃进程激烈争抢。从pidstat的输出中我们一目了然地看到nginx进程PID 12345每秒产生了 15 万次自愿上下文切换。这说明nginx的 worker 进程在疯狂地等待某个资源导致它们不断地被挂起和唤醒。可能的原因后端服务瓶颈nginx作为反向代理后端应用如 PHP、Java处理缓慢导致nginxworker 大量阻塞在网络 I/O 上。锁竞争应用代码中存在设计不当的全局锁导致大量线程在等待同一个锁。连接数过多nginx配置的worker_connections过高而系统资源如文件描述符不足。通过这个线索开发和运维团队就可以进一步深入到nginx的配置和后端应用代码中找到最终的性能瓶颈。 神秘的内核线程kworker与ksoftirqd在使用top或ps时你经常会看到一些以k开头的、方括号括起来的进程如kworker和ksoftirqd。它们是内核线程 (Kernel Threads)在后台为操作系统执行各种管理任务。kworker内核的“临时工”kworker是内核工作队列workqueue的执行者。内核的各个子系统如磁盘、网络、定时器会把一些耗时较长、不能在中断上下文中完成的任务打包成一个“工作项”扔到工作队列里然后由kworker线程在未来的某个时刻异步地去执行。命名格式kworker/ucpu_id:worker_id或kworker/cpu_id:worker_idu表示这个kworker是非绑定的unbound可以在多个 CPU 核心间迁移。cpu_id表示它主要在哪个 CPU 上活动。如果kworker的 CPU 使用率很高通常意味着内核正在忙于处理某些后台任务。你可以通过perf工具来追踪kworker到底在忙什么。ksoftirqd软中断的“清道夫”当硬件中断硬中断发生得过于频繁时为了避免长时间关中断影响系统响应内核会将一部分耗时较长的处理工作推迟变成软中断 (softirq)。ksoftirqd就是专门用来处理软中断的内核线程每个 CPU 核心都有一个。如果软中断的产生速度超过了处理速度积压的软中断就会由ksoftirqd来“兜底”处理。命名格式ksoftirqd/cpu_id如果你在top中看到sisoftirq或者ksoftirqd的 CPU 使用率很高通常指向以下问题网络风暴网络收发包极其频繁导致大量的网络软中断。内核锁竞争内核中处理软中断的逻辑遇到了锁竞争导致处理效率下降。 总结与展望上下文切换是核心成本过高的cs值是系统“内耗”的明确信号。使用vmstat发现问题再用pidstat -w定位到具体进程。区分自愿与非自愿切换cswch/s高通常指向 I/O 或锁等待问题nvcswch/s高通常指向 CPU 资源不足或争抢激烈。关注内核线程kworker和ksoftirqd的高 CPU 占用率是内核层面存在瓶颈的线索通常与 I/O 和网络活动密切相关。下一篇预告在本章中我们学会了如何量化和定位上下文切换带来的性能损耗。我们不仅掌握了vmstat和pidstat这两个强大的诊断工具还认识了kworker和ksoftirqd这两个重要的内核“打工人”。在下一章我们将继续深入 CPU 的世界专门探讨软中断和硬中断。我们将揭示top命令中hi和si的真正含义并学习如何处理由它们引发的性能问题。敬请期待