环境:Windows 11 + PowerShell 7.6.5 + Windows Terminal 1.24 症状:每次打开 PowerShell 7,要等十几秒才出现输入行 结论:国内网络下
ctldl.windowsupdate.com不可达,导致证书信任列表(CTL)下载超时 15 秒。关闭根证书自动更新后彻底解决。
一、排查过程
1. 第一嫌疑人:PowerToys CommandNotFound 模块
打开 profile 检查(路径:C:\Users\<用户名>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1),发现里面有:
Import-Module -Name Microsoft.WinGet.CommandNotFound
实测数据:
| 场景 | 耗时 |
|---|---|
pwsh -NoProfile -Command "exit" | ~0.3 秒 |
pwsh -Command "exit"(带 profile) | ~17 秒 |
| 冷启动单独导入该模块 | ~58 秒 |
这是 PowerToys 的"命令未找到时建议 winget 安装"功能,启动时要读 winget 索引,是已知慢点。
处理:卸载 PowerToys、清空 profile、删除模块残留目录:
C:\Users\<用户名>\Documents\PowerShell\Modules\Microsoft.WinGet.CommandNotFound
注意:仅删 profile 里的
Import-Module行即可解决启动慢,模块文件残留不影响速度。另外 PowerToys 卸载后注册表里可能仍有残留条目,属正常现象。
2. 第二嫌疑人:PSReadLine
清完之后依然要十几秒。进一步测试:
# 交互模式(会加载 PSReadLine)
"exit" | pwsh -NoLogo # 15.4 秒
# 非交互模式
pwsh -NoProfile -Command "..." # 0.3 秒
# 换其他模块对照
pwsh -NoProfile -Command "Import-Module CimCmdlets" # 0.4 秒
pwsh -NoProfile -Command "Import-Module PSReadLine" # 15.4 秒
规律非常明显:
精确 15 秒,每次都是
卡住期间 CPU 几乎无占用
只有 PSReadLine 这类签名模块慢,普通模块不慢
Get-PSDrive在新进程首次运行也同样卡 15 秒
这是典型的网络 I/O 超时特征,而不是计算慢。
3. 官方文档的线索
PowerShell 官方文档《Troubleshoot PowerShell startup issues》明确记载:
如果
pwsh -noninteractive启动很快,那么问题很可能出在证书吊销列表(CRL)检查上。.NET 运行时在验证数字签名时会检查 CRL 以确保证书仍然有效。在断网环境下无法访问互联网上的 CRL,默认超时是 15 秒。
先按文档尝试了降低超时的注册表方案:
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CertDllCreateCertificateChainEngine\Config" -Name "ChainUrlRetrievalTimeoutMilliseconds" -Value 1000 -PropertyType DWord -Force
没有效果——说明卡的不是 CRL 本身,而是别的东西。
4. 抓现场:卡住时进程在连谁
启动一个卡住的进程,第 6 秒时抓它的 TCP 连接:
$p = Start-Process pwsh -ArgumentList '-NoProfile','-Command','Import-Module PSReadLine' -PassThru -WindowStyle Hidden
Start-Sleep -Seconds 6
Get-NetTCPConnection -OwningProcess $p.Id
结果:
RemoteAddress RemotePort State
223.106.78.79 80 SynSent
进程在等一个永远握手不成功的 HTTP 连接。223.106.78.79 不是微软的 IP(DNS 污染返回的假地址)。
5. 定位域名
对比系统 DNS 和公共 DNS 的解析结果:
ctldl.windowsupdate.com
系统DNS: 223.106.78.78, 223.87.195.118, 223.106.78.79 ← 假 IP
阿里DNS: 117.68.54.123 ... ← 也连不上
真凶就是 ctldl.windowsupdate.com——Windows 下载"证书信任列表(Certificate Trust List)“的服务器。PowerShell 新进程做证书链验证时,Windows 会先去下载最新的 CTL,在你的网络环境下这个域名根本连不上,于是每次都卡满 15 秒超时。
换 DNS 也没用:两个渠道返回的 IP 实测均不可达。
二、最终修复
在管理员终端运行,关闭根证书自动更新:
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot" -Name "DisableRootAutoUpdate" -Value 1 -PropertyType DWord -Force
效果:启动从 15+ 秒恢复到 0.3 秒左右。
安全性说明:
系统已内置的根证书全部继续有效,正常上网不受影响
根证书也会随 Windows 大版本更新而刷新
损失的只是"自动增量下载新根证书”,而这一步在你的网络下本来就从没成功过(一直在超时),等于零损失
附:之前改的 CRL 超时可以还原
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CertDllCreateCertificateChainEngine\Config" -Name "ChainUrlRetrievalTimeoutMilliseconds"
三、以后想更新根证书怎么办
方法1:一键拉取全部根证书(推荐)
# 从 Windows Update 下载全部受信任根证书,打包成 roots.sst
certutil -generateSSTFromWU "$env:USERPROFILE\Desktop\roots.sst"
# 导入到本机根证书存储
Import-Certificate -FilePath "$env:USERPROFILE\Desktop\roots.sst" -CertStoreLocation Cert:\LocalMachine\Root
不受 DisableRootAutoUpdate 策略影响,是独立下载通道。
方法2:按需导入单个根证书
实际中只有访问某网站报证书错误时才需要。去对应 CA 官网下载根证书(.cer),然后:
Import-Certificate -FilePath "证书.cer" -CertStoreLocation Cert:\LocalMachine\Root
方法3:临时恢复自动更新
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot" -Name DisableRootAutoUpdate -Value 0
certutil -pulse # 立即触发一次
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot" -Name DisableRootAutoUpdate -Value 1
提醒:以上方法本质都从
ctldl.windowsupdate.com/ Windows Update 下载,在网络不畅时依然会超时。可行姿势:哪天挂了代理或换了网络环境,跑一次方法1一步到位。
四、排查方法论总结
- 先测基线:
pwsh -NoProfilevs 带_profile,隔离 profile 因素 - 对照实验:换模块、换命令,找出"只有 X 慢"的规律
- 看数字特征:精确 15 秒 + 零 CPU = 网络/IO 超时,不是代码慢
- 抓现场:卡住瞬间用
Get-NetTCPConnection -OwningProcess看它在连谁 - 查 DNS:系统 DNS vs 公共 DNS 对比,识别污染
- 善用官方文档:PowerShell 文档对启动慢有专门章节,“非交互快 = CRL 检查"这条经验直接命中方向
常用排查命令速查
# 对比有无 profile 的启动耗时
"无Profile: " + (Measure-Command { pwsh -NoProfile -Command "exit" }).TotalMilliseconds + " ms"
"有Profile: " + (Measure-Command { pwsh -Command "exit" }).TotalMilliseconds + " ms"
# 测模块加载耗时
Measure-Command { pwsh -NoProfile -Command "Import-Module PSReadLine" }
# 抓卡住进程的网络连接
Get-NetTCPConnection -OwningProcess <PID>
# 对比 DNS 解析
Resolve-DnsName ctldl.windowsupdate.com
Resolve-DnsName ctldl.windowsupdate.com -Server 223.5.5.5