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—— 自動生成並套用豁免項目(預設預覽)