PowerShell 实现监控主流三条技术路线: 轮询查询进程快照(最简通用方案) Get-Process WMI/CIM 进程事件订阅(事件驱动,优于轮询) Register-CimIndicationEvent P/Invoke Win32 API(检测窗口无响应、进程冻结) user32.dll::IsHungAppWindow

PowerShell 监视程序、自动重启崩溃 / 挂起应用|完整拆解、底层原理、链路与配套组件

一、整体方案定位

这类监控脚本核心目标两类场景:
  1. 进程意外退出(崩溃):进程正常 / 异常终止,不存在 PID;
  2. 进程挂起(卡死、无响应):进程仍然存在,但窗口无响应、主线程阻塞(GUI 程序为主)。
PowerShell 实现监控主流三条技术路线:
  1. 轮询查询进程快照(最简通用方案) Get-Process
  2. WMI/CIM 进程事件订阅(事件驱动,优于轮询) Register-CimIndicationEvent
  3. P/Invoke Win32 API(检测窗口无响应、进程冻结) user32.dll::IsHungAppWindow
底层根基:Windows 进程管理器(ntoskrnl.exe Ps 子系统)+ Win32 窗口管理子系统。
关键区分:
 
✅ 进程崩溃:内核 Ps 子系统销毁 EPROCESS 对象;
 
✅ 程序挂起:进程存活,主线程消息循环阻塞,内核不会主动杀死进程。

二、完整调用栈树形结构

路线 1:轮询模式(Get-Process 最常用脚本方案)

plaintext
PowerShell(pwsh/powershell.exe)
    ↓ Get-Process Cmdlet
System.Diagnostics.Process (.NET托管封装)
    ↓ kernel32.dll
        ├─ OpenProcess
        ├─ EnumProcesses 枚举系统所有PID
        └─ QueryProcessInformation 获取进程基础信息
    ↓ 两种分支判断
├─分支A:目标进程不在列表 → 判定崩溃,启动新实例 Start-Process
│
└─分支B:进程存在 → 额外判断是否挂起
        ↓ Add-Type P/Invoke user32.dll::IsHungAppWindow
        ↓ Dwm.exe / win32k.sys 窗口消息队列检测
        ↓ 判定挂起 → 可选择Kill进程后重启

路线 2:CIM WMI 事件订阅(事件驱动,推荐生产)

plaintext
PowerShell
    ↓ Register-CimIndicationEvent
winmgmt.exe (WMI服务)
    ↓ root/cimv2 Win32_ProcessStopTrace 进程停止事件
    ↓ 内核Ps子系统 进程退出通知
当进程崩溃/正常关闭时,WMI主动推送事件
脚本触发重启逻辑,无需高频轮询
优势:无循环空耗 CPU;短板:只能感知进程退出,无法识别 “进程活着但卡死”。

三、核心功能模块底层原理拆解

模块 1:检测进程是否存在(识别崩溃)

API 链路

EnumProcesses() → 获取全部 PID 列表 → OpenProcess() 查询进程镜像名
 
.NET Get-Process 内部封装以上原生 API。
 
持久标识方式两种:
  1. 按进程名称(notepad.exe):简易,但存在同名多实例冲突;
  2. 按原始启动 PID:进程重启后 PID 改变,无法跟踪;
  3. 更可靠:记录启动时间 + 主窗口标题 + 镜像路径区分实例。

模块 2:检测 GUI 程序是否挂起(IsHungAppWindow)

c
 
运行
[DllImport("user32.dll")]
public static extern bool IsHungAppWindow(IntPtr hWnd);
底层原理:
 
user32.dll 向目标窗口投递消息,检测消息队列是否长期得不到处理;
 
阈值由系统内部会话管理器控制。
 
⚠️ 重要限制
  1. 仅适用于拥有顶层窗口的 GUI 程序;
  2. 控制台程序、后台服务、无窗口进程无法使用该 API 判断是否卡死;
  3. 后台服务阻塞、业务逻辑卡死,没有通用原生 API 识别,只能依靠「业务心跳端口 / 日志 / 自定义健康接口」。

模块 3:终止异常进程(挂起后清理)

powershell
$proc | Stop-Process -Force
链路:
 
TerminateProcess() kernel32.dll → 内核 PsTerminateProcess,强制销毁进程地址空间。

模块 4:启动应用(Start-Process)

封装 CreateProcessW(),创建新进程会话,继承安全令牌。

四、依赖文件、系统配套清单

用户态核心 DLL

  1. kernel32.dll:进程枚举、OpenProcess、TerminateProcess、CreateProcess
  2. user32.dll:IsHungAppWindow(窗口无响应检测)
  3. advapi32.dll:可选,查询进程安全信息、权限
  4. System.Diagnostics.Process (.NET 托管层,Get-Process 底层载体)

系统服务

  1. Winmgmt(WMI):使用 CIM 事件订阅方案必需;轮询方案不需要。
  2. RPC:WMI 通信依赖。

内核组件

  1. ntoskrnl.exe/ Ps 进程管理器:维护系统进程列表、EPROCESS 结构
  2. win32k.sys:窗口子系统、消息队列(GUI 挂起检测)

权限约束

  1. 监控普通应用:普通用户权限基本够用;
  2. 终止系统进程、服务进程、受保护进程:必须管理员权限;
  3. 部分受保护进程(杀毒、系统关键进程)OpenProcess 会被拒绝访问。

五、两种架构优劣对比

方案 A:While 循环 + Get-Process 轮询(通用脚本)

powershell
while($true){
    $proc = Get-Process "MyApp" -ErrorAction SilentlyContinue
    if(-not $proc){
        Start-Process "C:\MyApp.exe"
    }
    Start-Sleep 3
}
✅ 优点:兼容所有 Windows 版本;不需要 WMI;同时支持崩溃 + GUI 挂起检测
 
❌ 缺点:周期性轮询,存在最大「睡眠间隔」延时;高频轮询轻微消耗 CPU

方案 B:CIM 进程停止事件订阅(事件驱动)

powershell
Register-CimIndicationEvent -ClassName Win32_ProcessStopTrace ...
✅ 优点:事件触发,无轮询、实时性高、资源开销极低
 
❌ 致命短板:只能捕获进程退出,不能识别 “进程活着但卡死”;控制台无窗口程序无法判定挂起。

六、底层典型坑点(运维高频问题)

  1. 同名多实例程序误重启
     
    只按进程名称判断,一个实例崩溃会把所有实例全部重启。
     
    解决:记录完整程序路径区分实例,不要只靠 Name。
  2. IsHungAppWindow 对控制台程序无效
     
    控制台程序没有顶层 HWND 窗口句柄,无法检测卡死;
     
    解决方案:应用内置 TCP 心跳、日志文件轮询、自定义健康检测接口。
  3. 进程 “僵尸 / 孤立”、无法 Kill
     
    进程处于内核阻塞状态(驱动等待 IO),TerminateProcess 失败;只能重启电脑。
  4. 脚本自身崩溃 → 监控失效
     
    PowerShell 脚本本身没有监护;生产环境建议:
  • 将监控脚本注册为Windows 服务(nssm);
  • 双层监护:外部看门狗监控 powershell 进程。
  1. 快速反复重启造成风暴
     
    程序本身存在 Bug,启动立刻崩溃,脚本无限循环创建进程。
     
    工程化方案:增加冷却时间、失败计数器,连续失败 N 次暂停重启。
  2. 权限问题:脚本以普通用户运行,无法启动需要管理员的程序
     
    Start-Process 添加 -Verb RunAs 提升;或监控脚本本身以管理员身份常驻。

七、完整数据流示例(轮询监控 GUI 程序)

  1. PowerShell 进入 while 循环,Sleep 间隔 3 秒;
  2. 调用 Get-Process 枚举进程,查找目标程序;
  3. 情形 1:找不到进程 → 判断崩溃,调用 Start-Process 启动程序;
  4. 情形 2:进程存在,获取 MainWindowHandle;
  5. P/Invoke 调用 IsHungAppWindow 检测窗口是否无响应;
  6. 如果挂起:Stop-Process -Force 强制杀死;等待短暂延时;启动新进程;
  7. 写入日志,循环休眠等待下一轮检测。

八、工程扩展分层设计(可直接落地)

  1. 采集层:轮询 / CIM 事件
  2. 健康判断层
    • 基础:进程是否存在
    • 增强:GUI 窗口是否挂起
    • 业务层:TCP 端口连通性、HTTP 健康接口、日志更新检测
  3. 保护策略层:重启冷却、失败次数阈值、告警输出(事件日志 / 文件)
  4. 执行层:Kill 异常进程 → 延时 → 启动应用
  5. 日志层:记录崩溃时间、重启原因、前后进程 PID

PowerShell 脚本来监视并自动重新启动崩溃或挂起的应用程序。以下是一个简单的示例脚本,用于监视并重新启动特定的应用程序:

powershellCopy Code
# 设置要监视的应用程序名称
$applicationName = "YourApplication.exe"

# 设置检测时间间隔(秒)
$checkInterval = 5

# 循环监视应用程序状态
while ($true) {
    $process = Get-Process | Where-Object { $_.ProcessName -eq $applicationName }

    if (-not $process) {
        Write-Host "$applicationName 崩溃,正在重新启动..."
        Start-Process -FilePath "C:\Path\To\Your\Application.exe"
    }

    Start-Sleep -Seconds $checkInterval
}

在这个示例脚本中,您需要将 "YourApplication.exe" 替换为您要监视的应用程序的实际名称,并将 C:\Path\To\Your\Application.exe 替换为应用程序的实陵路径。

 

CMD 批处理脚本来监视并自动重新启动崩溃或挂起的应用程序。以下是一个简单的示例脚本,用于监视并重新启动特定的应用程序:

Copy Code
@echo off

:: 设置要监视的应用程序名称
set applicationName=YourApplication.exe

:: 设置检测时间间隔(秒)
set checkInterval=5

:: 循环监视应用程序状态
:loop
tasklist | findstr /i %applicationName% >nul

if errorlevel 1 (
    echo %applicationName% 崩溃,正在重新启动...
    start "" "C:\Path\To\Your\Application.exe"
)

timeout /t %checkInterval% /nobreak >nul
goto :loop

在这个示例脚本中,您需要将 YourApplication.exe 替换为您要监视的应用程序的实际名称,并将 C:\Path\To\Your\Application.exe 替换为应用程序的实际路径。

 

 

PowerShell 实现自动重新启动崩溃或挂起的服务,您可以按照以下步骤操作:

  1. 获取服务名称:

    • 确定您要监视和重新启动的服务的名称。可以使用 Get-Service cmdlet 来获取系统中所有服务的列表。
  2. 编写 PowerShell 脚本:

    • 编写一个 PowerShell 脚本来监视并重新启动服务。下面是一个简单的示例脚本:
powershellCopy Code
# 设置要监视和重新启动的服务名称
$serviceName = "YourServiceName"

# 设置检测时间间隔(秒)
$checkInterval = 5

# 循环监视服务状态
while ($true) {
    $service = Get-Service | Where-Object { $_.Name -eq $serviceName }

    if (-not $service.Status -eq "Running") {
        Write-Host "$serviceName 服务崩溃或挂起,正在重新启动..."
        Restart-Service -Name $serviceName
    }

    Start-Sleep -Seconds $checkInterval
}

在这个示例脚本中,您需要将 "YourServiceName" 替换为您要监视和重新启动的服务的实际名称。

  1. 运行脚本:
    • 将脚本保存为 .ps1 文件,并在管理员权限下运行 PowerShell 控制台。
    • 使用 Set-ExecutionPolicy RemoteSigned 命令来设置脚本执行策略,以允许运行本地脚本。
    • 运行您编写的 PowerShell 脚本来监视并重新启动服务。

 

posted @ 2024-02-20 14:13  suv789  阅读(357)  评论(0)    收藏  举报