Defender 高 CPU 排查与调优
这个 skill 解决什么问题
Windows 开发机上,Microsoft Defender 的实时保护对每一次文件读取都做扫描。当某个进程大量读文件(IDEA 索引、Codex 扫项目、find / 全盘遍历、Maven 拉依赖、WSL/Docker 内部 IO),Defender 会被喂成一条永不停歇的扫描流水线,MsMpEng.exe CPU 飙到 50%+,整机卡顿。
这套流程的核心洞察是:Defender 高 CPU 几乎从不是 Defender 本身坏了,而是某个文件 IO 大户在持续触发实时保护。所以排查的关键不是"调 Defender 参数",而是定位那个触发源,然后用精准的排除项把它从扫描路径上摘掉——既解决卡顿,又不动整体安全姿态。
排查流程
第 0 步:先确认是不是 Defender,以及"哪种口径"的 CPU
任务管理器(Task Manager)的进程 CPU 列与性能计数器 \Process(*)\% Processor Time 的口径不同:计数器在多核机器上单进程可超过 100%(每核 100% 累加),任务管理器会把计数器值除以核数显示。所以"任务管理器 61.2%"可能对应计数器 766%(20 核机器)。两边的数值都对,别拿错口径下结论。
快速确认当前 MsMpEng 实际占用(任务管理器口径):
$cores = $env:NUMBER_OF_PROCESSORS
$c = (Get-Counter '\Process(MsMpEng*)\% Processor Time').CounterSamples | Where-Object { $_.InstanceName -like 'msmpeng*' } | Select-Object -First 1
"MsMpEng: 计数器=$([math]::Round($c.CookedValue,1))% 等效任务管理器=$([math]::Round($c.CookedValue/$cores,1))% (机器 $cores 核)"
如果任务管理器显示高但计数器持续为 0,那是任务管理器的陈旧显示值没刷新——重开任务管理器即可,不要去改 Defender。
第 1 步:定位触发源(用相关性,不是直觉)
Defender 高 CPU 的根因分三类,定位方法不同:
| 根因 | 特征 | 怎么确认 |
|---|---|---|
| 实时保护被外部进程触发(最常见) | MsMpEng CPU 脉冲式波动,有外部进程同时在产生高 IO | 持续采样,找"MsMpEng 飙高时哪个进程 IO 也飙高" |
| 计划/后台扫描 | CPU 持续高位、波动小;ScanRunning 有值或计划任务在跑 | Get-MpComputerStatus 看 ScanRunning;Get-ScheduledTask -TaskPath '\Microsoft\Windows\Windows Defender\*' |
| Defender 自身后台任务(定义更新后校验、云采样等) | MsMpEng 自己 Read IO 最高,但没有对应的外部高 IO 进程 | 采样看 MsMpEng 自身 IO 是否远超其他进程 |
关键诊断手段:跑 scripts/diag-mpcpu.ps1。它持续采样若干秒,同时记录 MsMpEng CPU 和各进程 IO Data Bytes/sec。解读方法:
- 若某进程 IO 飙高时 MsMpEng 也飙高 → 该进程是触发源,走第 2 步治本
- 若 MsMpEng 自己 Read 最高、无外部高 IO → 走第 3 步(后台任务/扫描)
- 若 MsMpEng 全程接近 0 → 问题已不在 Defender,去别处找(用户看到的可能是任务管理器陈旧值)
第 2 步:治本——给触发源加排除项
排除项有两类,理解区别很重要:
- 进程排除(ExclusionProcess):指定进程名(如
idea64.exe),该进程读文件时不触发扫描,但文件被别的进程访问时仍扫描。精准、安全,优先用这个。 - 路径排除(ExclusionPath):指定目录,该目录下所有文件任何进程(包括浏览器、EDR)访问都不扫描。适合缓存/虚拟磁盘这类「就是个数据文件、谁碰都不该扫」的场景;不要用它来排除项目源码目录——项目源码应靠进程排除保护(只让开发工具读时不扫,浏览器下载到同目录仍扫)。这也是
gen-exclusions.ps1路径推导只产出缓存/虚拟磁盘/工具安装根、绝不自动排除项目目录的原因。
为什么进程排除优先:路径排除会让目录里所有文件对「所有进程」失去实时保护;进程排除只对「特定工具」放开,攻击面小得多。例如排除 codex.exe 进程,只是让 Codex 读你项目时不扫,但浏览器下载到同目录的文件仍会被扫——这就是我们想要的。
跑 scripts/gen-exclusions.ps1 —— 它扫描当前运行的开发类进程,匹配已知工具模式,自动生成推荐的进程+路径排除项,默认 DryRun 预览(列出建议+理由),确认后加 -Apply 才写入 Defender。它幂等、运行前备份当前排除项。
第 3 步:后台扫描类根因的处理
如果是计划扫描 / 自动维护在跑(ScanRunning 有值、计划任务 State=Running),通常等它跑完即可,不该硬杀——这是 Defender 在做必要的安全维护。如果反复在你不方便的时间触发,调整计划:
# 查看计划扫描
Get-ScheduledTask -TaskPath '\Microsoft\Windows\Windows Defender\*'
# 调整快速扫描时间(例:每天凌晨 3 点)——注意会覆盖既定的计划扫描配置
Set-MpPreference -ScanScheduleQuickScanTime 03:00:00
如果是定义更新后的全量校验(偶尔发生),通常一次性,跑完就好。
第 4 步:验证
加完排除项后,重新触发原来的高 IO 负载(重开 IDE 索引、再跑一次 find、让 Codex 扫项目),再跑一次 diag-mpcpu.ps1。成功的标准是:同样的 IO 负载下,MsMpEng 从原来的高位降到接近 0。如果没降,说明排除项没覆盖到真正的触发源——回到第 1 步看 diag 输出里哪个进程还在同时飙高。
安全边界——这些绝不排除
排除项会降低对应路径/进程的实时保护,所以必须有边界。以下绝不加入排除项,无论看起来多"省 CPU"(完整且权威的清单以 scripts/gen-exclusions.ps1 的 $denyProcs 和 $denyPathPrefix 为单一真源,文档列表仅作说明):
- 系统进程:
svchost.exe、dllhost.exe、smartscreen.exe、conhost.exe、lsass.exe、csrss.exe等 - 浏览器及其 WebView:
msedge.exe、chrome.exe、msedgewebview2.exe—— 浏览器下载是恶意软件的主要入口,保留扫描 - 安全/管控客户端:公司装的 EDR、VPN 客户端(如深信服 ECAgent、CrowdStrike、SentinelOne)—— 动它们可能触发合规告警,且本就是安全防线
- 整个用户目录 / 系统盘根:粒度太粗,等于裸奔
*.jar、*.exe、*.dll、*.ps1等可执行类扩展名:可执行,保留扫描。扩展名排除只用于纯编译产物(*.class)或日志(*.log)这类不可执行的
判断原则:排除项要瞄准"可信的开发工具在干它本职工作时读的文件",而不是"为了省 CPU 排除一切"。
进阶诊断:Performance Recorder
当 diag 脚本仍定位不准时,用 Defender 官方的性能记录器(需管理员)——它会给出 MsMpEng 把时间花在哪些具体路径/进程上的精确报告:
New-MpPerformanceRecord -RecordTo C:\perf.mpdr # 采集 60s(期间复现高 CPU)
# 报告会指出最耗 CPU 的路径与进程,据此精准加排除项
参考文件
references/common-exclusions.md—— 常见开发工具的推荐排除项分类清单(IDE/Java/Node/WSL/Docker/AI 编码助手/构建缓存),含每类的安全权衡说明。生成排除项时用它核对覆盖面。scripts/diag-mpcpu.ps1—— 诊断采样脚本scripts/gen-exclusions.ps1—— 自动生成并应用排除项(默认预览)