using Ayay.SerilogLogs; using Microsoft.Extensions.Hosting; using Serilog; using SHH.CameraSdk; using System.Diagnostics; namespace SHH.CameraService; /// /// 父进程守护服务 (BackgroundService) /// 核心逻辑:定期检查启动本服务的父进程是否存活,若父进程退出(如 UI 崩溃),则触发本服务自动退出,避免孤儿进程占用相机硬件资源。 /// public class ParentProcessSentinel : BackgroundService { private readonly ServiceConfig _config; private readonly IHostApplicationLifetime _lifetime; private ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core); /// /// 使用统一的结构化日志记录器,SourceContext 设置为 Core 模块 /// public ParentProcessSentinel( ServiceConfig config, IHostApplicationLifetime lifetime) { _config = config; _lifetime = lifetime; } /// /// 执行后台守护逻辑 /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { int pid = _config.ParentPid; // 1. 验证 PID 合法性。如果 PID 为 0 或负数,可能是手动启动调试模式,不执行守护逻辑 if (pid <= 0) { _sysLog.Warning("[Sentinel] 未指定有效的父进程 PID ({ParentPid}),守护模式已禁用,服务将持续运行.", pid); return; } _sysLog.Information("[Sentinel] 父进程守护已启动,正在监控目标 PID: {ParentPid}", pid); while (!stoppingToken.IsCancellationRequested) { if (!IsParentRunning(pid)) { _sysLog.Warning("[Sentinel] ### ALERT ### 检测到父进程 (PID:{ParentPid}) 已退出!正在下发系统终止信号...", pid); // 触发程序优雅退出 _lifetime.StopApplication(); // 强制跳出循环 break; } // 每 2 秒检查一次,避免 CPU 浪费 await Task.Delay(2000, stoppingToken); } } /// /// 核心状态判定:通过 PID 获取进程快照并检查存活状态 /// /// 父进程 ID /// 存活返回 True,已消亡返回 False private bool IsParentRunning(int pid) { try { // 尝试获取进程对象 var process = Process.GetProcessById(pid); // 检查是否已退出 if (process.HasExited) return false; return true; } catch (ArgumentException) { // GetProcessById 在找不到 PID 时会抛出 ArgumentException // 说明进程已经不存在了 return false; } catch (Exception ex) { _sysLog.Debug("[Sentinel] 无法定位 PID 为 {ParentPid} 的进程,判定为已退出.", pid); return true; // 发生未知错误时,保守起见认为它还活着 } } }