进程检测能力调整

This commit is contained in:
2026-08-07 22:43:27 +08:00
parent 5f028dae4c
commit acf15fe932
+62 -33
View File
@@ -399,7 +399,6 @@ func (pm *ProcessManager) isFRPCProcess(pid int) (bool, string) {
// 进程存活检测
// ================================================================
// isProcessAlive 检查 PID 对应的进程是否存活
func (pm *ProcessManager) isProcessAlive(pid int) bool {
if pid <= 0 {
return false
@@ -421,6 +420,50 @@ func (pm *ProcessManager) isProcessAlive(pid int) bool {
return process.Signal(syscall.Signal(0)) == nil
}
// ================================================================
// 进程清理
// ================================================================
func (pm *ProcessManager) killProcess(pid int) error {
if pid <= 0 {
return nil
}
if runtime.GOOS == "windows" {
cmd := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(pid))
return cmd.Run()
}
process, err := os.FindProcess(pid)
if err != nil {
return err
}
return process.Kill()
}
func (pm *ProcessManager) cleanupOrphans() {
// 检查端口是否被占用
occupied, err := pm.CheckPort()
if err != nil || !occupied {
return
}
// 检查是否有 PID 文件
pid := pm.readPIDFile()
if pid > 0 && pm.isProcessAlive(pid) {
return
}
log.Printf("[WARN] 检测到孤儿 frpc 进程 (端口 %d 被占用但无有效 PID),正在清理...", pm.adminPort)
if runtime.GOOS == "windows" {
exec.Command("taskkill", "/F", "/IM", "frpc.exe").Run()
} else {
exec.Command("pkill", "-f", "frpc").Run()
}
pm.deletePIDFile()
time.Sleep(1 * time.Second)
}
// ================================================================
// PID 文件操作
// ================================================================
@@ -532,6 +575,10 @@ func (pm *ProcessManager) Start(ctx context.Context) error {
return fmt.Errorf("获取锁失败: %w", err)
}
defer pm.Unlock()
// 清理孤儿进程
pm.cleanupOrphans()
return pm.startLocked(ctx)
}
@@ -557,45 +604,28 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
return fmt.Errorf("启动 frpc 失败: %w", err)
}
// ============================================================
// 组合状态检测:最多等待 5 秒,三条件全部满足才算成功
// ============================================================
// 立即写入 PID 文件
if err := pm.writePIDFile(cmd.Process.Pid); err != nil {
log.Printf("[WARN] 写入 PID 文件失败: %v", err)
}
log.Printf("[DEBUG] frpc 进程已启动,PID: %d", cmd.Process.Pid)
// 检测端口是否监听(最多 5 秒)
for attempt := 0; attempt < StartupMaxAttempts; attempt++ {
// 条件1: 检查 PID 文件
pid := pm.readPIDFile()
if pid <= 0 {
time.Sleep(StartupRetryDelay)
continue
}
// 条件2: 检查进程是否存活
if !pm.isProcessAlive(pid) {
time.Sleep(StartupRetryDelay)
continue
}
// 条件3: 检查端口是否可连接
occupied, err := pm.CheckPort()
if err == nil && occupied {
// 三项全部满足!
pm.writePIDFile(pid)
log.Printf("[INFO] frpc 启动成功 (PID: %d, 端口: %d),耗时 %dms",
pid, pm.adminPort, attempt*int(StartupRetryDelay/time.Millisecond))
cmd.Process.Pid, pm.adminPort, attempt*int(StartupRetryDelay/time.Millisecond))
return nil
}
time.Sleep(StartupRetryDelay)
}
// 超时:输出诊断信息
pid := pm.readPIDFile()
if pid > 0 {
alive := pm.isProcessAlive(pid)
occupied, _ := pm.CheckPort()
return fmt.Errorf("frpc 启动超时: PID=%d, 进程存活=%v, 端口监听=%v", pid, alive, occupied)
}
return fmt.Errorf("frpc 启动超时: 未生成 PID 文件")
// 端口监听超时:杀掉启动的进程并清理
log.Printf("[WARN] frpc 启动超时 (PID: %d),正在清理...", cmd.Process.Pid)
pm.killProcess(cmd.Process.Pid)
pm.deletePIDFile()
return fmt.Errorf("frpc 启动超时: 端口未监听 (PID: %d)", cmd.Process.Pid)
}
func (pm *ProcessManager) Stop(ctx context.Context) error {
@@ -652,8 +682,7 @@ func (pm *ProcessManager) stopLocked(_ context.Context) error {
// 端口未释放, 强制 kill
if runtime.GOOS == "windows" {
cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe")
cmd.Run()
exec.Command("taskkill", "/F", "/IM", "frpc.exe").Run()
} else {
proc.Kill()
}