适用于 PowerShell 7.0 及以上版本
在服务器运维和 DevOps 实践中,系统性能监控是保障业务稳定运行的基石。无论是排查突发的性能抖动,还是进行容量规划和趋势分析,都需要一套可靠的监控手段。传统的 GUI 工具(如任务管理器、perfmon)虽然直观,但不适合自动化场景和大规模服务器管理。
PowerShell 提供了对 WMI/CIM 类、.NET 性能计数器和系统 API 的完整访问能力,让我们可以用脚本化的方式采集、分析和导出系统性能数据。本文将介绍如何使用 PowerShell 构建一套实用的系统性能监控方案,涵盖 CPU、内存、磁盘、进程、网络等关键指标。
采集 CPU、内存和磁盘基础指标 系统监控的第一步是获取核心资源的使用情况。通过 CIM 类可以高效地采集 CPU 利用率、内存使用率和磁盘空间信息,这些是判断系统健康状态最基本的指标。
下面的函数将 CPU、内存和磁盘三项指标整合到一个对象中,方便后续统一处理和比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function Get-SystemPerformanceSnapshot { $cpu = Get-CimInstance -ClassName Win32_Processor | Measure-Object -Property LoadPercentage -Average | Select-Object -ExpandProperty Average $os = Get-CimInstance -ClassName Win32_OperatingSystem $totalMemoryGB = [math ]::Round($os .TotalVisibleMemorySize / 1 MB, 2 ) $freeMemoryGB = [math ]::Round($os .FreePhysicalMemory / 1 MB, 2 ) $usedMemoryPct = [math ]::Round( ($os .TotalVisibleMemorySize - $os .FreePhysicalMemory) / $os .TotalVisibleMemorySize * 100 , 1 ) $disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object { $freeGB = [math ]::Round($_ .FreeSpace / 1 GB, 2 ) $totalGB = [math ]::Round($_ .Size / 1 GB, 2 ) $usedPct = [math ]::Round(($_ .Size - $_ .FreeSpace) / $_ .Size * 100 , 1 ) [PSCustomObject ]@ { Drive = $_ .DeviceID TotalGB = $totalGB FreeGB = $freeGB UsedPct = $usedPct } } [PSCustomObject ]@ { Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" CpuUsagePct = $cpu TotalMemoryGB = $totalMemoryGB FreeMemoryGB = $freeMemoryGB MemoryUsedPct = $usedMemoryPct Disks = $disks } } Get-SystemPerformanceSnapshot
执行结果示例:
1 2 3 4 5 6 7 Timestamp : 2025-04-22 09:15:30 CpuUsagePct : 23 TotalMemoryGB : 31.89 FreeMemoryGB : 14.52 MemoryUsedPct : 54.5 Disks : {@{Drive =C:; TotalGB =476.68; FreeGB =218.35; UsedPct =54.2}, @{Drive =D:; TotalGB =931.51; FreeGB =612.08; UsedPct =34.3}}
可以看到,一条命令就能拿到系统当前的核心资源状态。当 CPU 或内存使用率超过阈值时,运维人员可以第一时间感知并介入。
进程监控与资源排行 系统性能异常往往由个别进程引起。通过分析进程的资源占用情况,可以快速定位问题根源——是内存泄漏、CPU 密集计算,还是磁盘 I/O 瓶颈。
下面这段脚本按 CPU 和内存占用分别列出 Top N 进程,并标记出超出阈值的高消耗进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function Get-TopProcesses { param ( [int ]$Top = 10 , [double ]$CpuThreshold = 80 , [double ]$MemThresholdMB = 500 ) $processes = Get-Process | Where-Object { $_ .Id -gt 0 } | Select-Object Id, ProcessName, @ {N='CPU_Sec' ; E={[math ]::Round($_ .CPU, 2 )}}, @ {N='Memory_MB' ; E={[math ]::Round($_ .WorkingSet64 / 1 MB, 2 )}}, @ {N='Threads' ; E={$_ .Threads.Count}}, StartTime $byCpu = $processes | Sort-Object CPU_Sec -Descending | Select-Object -First $Top $byMem = $processes | Sort-Object Memory_MB -Descending | Select-Object -First $Top $alerts = $processes | Where-Object { $_ .CPU_Sec -gt $CpuThreshold -or $_ .Memory_MB -gt $MemThresholdMB } [PSCustomObject ]@ { TopByCpu = $byCpu TopByMem = $byMem Alerts = $alerts } } $result = Get-TopProcesses -Top 5 $result .TopByCpu | Format-Table -AutoSize
执行结果示例:
1 2 3 4 5 6 7 -- ----------- ------- --------- ------- --------- . . . . . . . . . .
在日常运维中,将此脚本放入定时任务,每隔几分钟运行一次,就能持续跟踪进程资源变化趋势。当某个进程突然飙升至告警阈值之上,可以及时触发通知。
网络连接统计与异常检测 网络连接状态是排查服务可用性和安全事件的重要依据。大量 TIME_WAIT 连接可能意味着短连接风暴,异常的外连 IP 可能暗示安全风险,某个端口连接数暴涨可能表示正在遭受攻击或业务流量激增。
以下脚本通过 Get-NetTCPConnection 统计连接状态分布和端口连接数,帮助快速发现网络层面的异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 function Get-NetworkConnectionStats { $connections = Get-NetTCPConnection | Where-Object { $_ .State -ne 'Bound' } $stateStats = $connections | Group-Object State | Sort-Object Count -Descending | ForEach-Object { [PSCustomObject ]@ { State = $_ .Name Count = $_ .Count } } $portStats = $connections | Where-Object { $_ .LocalPort -gt 0 } | Group-Object LocalPort | Sort-Object Count -Descending | Select-Object -First 10 | ForEach-Object { [PSCustomObject ]@ { Port = $_ .Name Count = $_ .Count } } $remoteIps = $connections | Where-Object { $_ .RemoteAddress -and $_ .RemoteAddress -ne '0.0.0.0' -and $_ .RemoteAddress -ne '::' } | Group-Object RemoteAddress | Sort-Object Count -Descending | Select-Object -First 5 | ForEach-Object { [PSCustomObject ]@ { RemoteIP = $_ .Name Count = $_ .Count } } [PSCustomObject ]@ { ConnectionStates = $stateStats TopPorts = $portStats TopRemoteIps = $remoteIps TotalConnections = $connections .Count } } $stats = Get-NetworkConnectionStats $stats .ConnectionStates | Format-Table -AutoSize
执行结果示例:
当 TIME_WAIT 或 CLOSE_WAIT 连接数异常增多时,往往意味着应用层的连接管理存在问题。结合远端 IP 统计,还能识别出是否存在异常的频繁外连行为。
数据导出为 CSV 和 JSON 监控数据如果不能持久化存储,就只能在当下查看,无法做历史趋势分析。将采集到的性能数据导出为 CSV 或 JSON 格式,既方便导入 Excel 做图表,也便于与 Grafana、ELK 等监控平台集成。
下面这段代码展示了如何将性能快照追加写入 CSV 文件,以及一次性导出为结构化的 JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function Export-PerformanceData { param ( [string ]$OutputDir = "$HOME \PerfLogs" ) if (-not (Test-Path $OutputDir )) { New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null } $snapshot = Get-SystemPerformanceSnapshot $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $flatData = [PSCustomObject ]@ { Timestamp = $snapshot .Timestamp CpuUsagePct = $snapshot .CpuUsagePct MemoryUsedPct = $snapshot .MemoryUsedPct FreeMemoryGB = $snapshot .FreeMemoryGB DiskInfo = ($snapshot .Disks | ForEach-Object { "$ ($_ .Drive)=$ ($_ .UsedPct)%" }) -join '; ' } $csvPath = Join-Path $OutputDir "perf_$ (Get-Date -Format 'yyyyMMdd').csv" $flatData | Export-Csv -Path $csvPath -NoTypeInformation -Append $jsonPath = Join-Path $OutputDir "perf_$timestamp .json" $snapshot | ConvertTo-Json -Depth 3 | Set-Content -Path $jsonPath [PSCustomObject ]@ { CsvFile = $csvPath JsonFile = $jsonPath Written = $true } } Export-PerformanceData
执行结果示例:
1 2 3 CsvFile : /home/u ser/PerfLogs/ perf_20250422.csv JsonFile : /home/u ser/PerfLogs/ perf_20250422_091530.json Written : True
CSV 追加模式让每次采集的行数据逐行写入同一个文件,配合 Excel 或 Python 脚本即可绘制性能趋势曲线。JSON 格式则保留了完整的嵌套结构,适合程序化消费。
持续监控与阈值告警 单次采集只能看到瞬时状态,真正的运维监控需要持续轮询并在指标异常时主动告警。下面这段脚本实现了一个轻量级的持续监控循环,支持 CPU、内存、磁盘三个维度的阈值检测,并在超限时输出告警信息。
你可以将告警逻辑替换为发送邮件、调用 Webhook 或写入事件日志,实现完整的告警链路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 function Start-PerformanceWatch { param ( [int ]$IntervalSeconds = 30 , [double ]$CpuThreshold = 85 , [double ]$MemThreshold = 90 , [double ]$DiskThreshold = 90 , [int ]$DurationMinutes = 10 ) $endTime = (Get-Date ).AddMinutes($DurationMinutes ) $alertCount = 0 Write-Host "性能监控已启动,持续 $DurationMinutes 分钟,间隔 $ {IntervalSeconds}s" ` -ForegroundColor Cyan Write-Host ("CPU>{0}% | MEM>{1}% | DISK>{2}% 触发告警`n" -f ` $CpuThreshold , $MemThreshold , $DiskThreshold ) -ForegroundColor DarkGray while ((Get-Date ) -lt $endTime ) { $snap = Get-SystemPerformanceSnapshot $alerts = @ () if ($snap .CpuUsagePct -gt $CpuThreshold ) { $alerts += "CPU 使用率 $ ($snap .CpuUsagePct)% 超过阈值 $ {CpuThreshold}%" } if ($snap .MemoryUsedPct -gt $MemThreshold ) { $alerts += "内存使用率 $ ($snap .MemoryUsedPct)% 超过阈值 $ {MemThreshold}%" } foreach ($disk in $snap .Disks) { if ($disk .UsedPct -gt $DiskThreshold ) { $alerts += "磁盘 $ ($disk .Drive) 使用率 $ ($disk .UsedPct)% 超过阈值 $ {DiskThreshold}%" } } $timeStr = Get-Date -Format "HH:mm:ss" if ($alerts .Count -gt 0 ) { $alertCount += $alerts .Count Write-Host "[$timeStr ] ALERT:" -ForegroundColor Red -NoNewline Write-Host " $ ($alerts -join ' | ')" -ForegroundColor Yellow } else { Write-Host "[$timeStr ] OK - CPU:$ ($snap .CpuUsagePct)% MEM:$ ($snap .MemoryUsedPct)%" ` -ForegroundColor Green } Start-Sleep -Seconds $IntervalSeconds } Write-Host "`n监控结束,共触发 $alertCount 条告警。" -ForegroundColor Cyan } Start-PerformanceWatch -IntervalSeconds 15 -DurationMinutes 2
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 性能监控已启动,持续 2 分钟,间隔 15 s CPU >85 [09 :15 :00 ] OK - CPU :23 [09 :15 :15 ] OK - CPU :28 [09 :15 :30 ] ALERT : CPU 使用率 92 [09 :15 :45 ] OK - CPU :31 [09 :16 :00 ] OK - CPU :25 [09 :16 :15 ] OK - CPU :22 [09 :16 :30 ] OK - CPU :27 [09 :16 :45 ] OK - CPU :24 监控结束,共触发 1 条告警。
在实际生产环境中,可以将此脚本作为 Windows 计划任务或 systemd 服务运行,配合邮件通知模块(Send-MailMessage)或 Webhook(Invoke-RestMethod)将告警推送到运维群。
注意事项
CIM vs WMI :优先使用 Get-CimInstance 而非已弃用的 Get-WmiObject,CIM 支持远程会话复用,性能更好
采集频率 :间隔不宜过短(建议不低于 10 秒),频繁采集本身会消耗 CPU,尤其在旧设备上
远程监控 :结合 PowerShell Remoting,可以在一台管理机上统一采集多台服务器的性能数据,使用 -ComputerName 参数即可
权限要求 :部分 CIM 类和性能计数器需要管理员权限才能访问,脚本应以提升权限运行
数据保留 :CSV 追加模式下注意定期归档和清理旧文件,避免单个文件过大影响读取性能
跨平台差异 :PowerShell 7 在 Linux/macOS 上部分 CIM 类不可用,需使用 /proc 文件系统或 Get-Process 等替代方案