You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go CLI工具信号处理问题:关闭终端窗口未触发清理函数

解决方案:处理终端关闭时的信号触发问题

1. 扩展监听的信号集合

不同终端(如iTerm)关闭窗口时发送的信号可能与MacOS默认终端不同,除已监听的信号外,需添加更多可捕获的终端相关信号:

修改信号监听代码为:

signal.Notify(signals, 
    syscall.SIGINT, 
    syscall.SIGTERM, 
    syscall.SIGHUP, 
    syscall.SIGQUIT, 
    syscall.SIGABRT,
    os.Interrupt)

2. 调整进程组设置

终端关闭时通常会向前台进程组发送信号,若程序未成为进程组组长,可能无法接收信号。在main函数开头添加进程组设置:

func main() {
    // 将当前进程设为新进程组的组长
    if err := syscall.Setpgid(0, 0); err != nil {
        logrus.Warnf("Failed to set process group: %v", err)
    }
    // ... 原有代码逻辑
}

该操作确保终端关闭时,信号能正确传递到目标进程。

3. 直接在信号处理goroutine中执行清理

避免通道传递可能引发的阻塞,直接在信号处理逻辑中调用cleanup后退出:

go func() {
    sig := <-signals
    fmt.Printf("\nReceived signal: %v, starting cleanup...\n", sig)
    cleanup()
    os.Exit(0)
}()

跳过主goroutine的通道等待,确保清理逻辑立即触发。

4. 验证信号发送情况

若以上方法无效,可先确认iTerm关闭时发送的信号类型:

  • 运行程序后,用ps aux | grep <程序名>获取进程PID
  • 关闭iTerm窗口,同时在另一个终端执行pkill -l -P <父进程PID>查看发送的信号
    根据实际捕获的信号,补充到监听列表中。

修改后的完整测试代码

package main

import (
    "fmt"
    "os"
    "syscall"
    "os/signal"
    "os/exec"
    "github.com/sirupsen/logrus"
)

func main() {
    // 设置当前进程为新进程组的组长
    if err := syscall.Setpgid(0, 0); err != nil {
        logrus.Warnf("Failed to set process group: %v", err)
    }

    // Parse the command-line argument.
    if len(os.Args) < 2 {
        logrus.Fatalf("Missing required argument")
    }
    arg := os.Args[1]

    logrus.Infof("Running with argument: %s", arg)

    // Set up signal handling.
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, 
        syscall.SIGINT, 
        syscall.SIGTERM, 
        syscall.SIGHUP, 
        syscall.SIGQUIT, 
        syscall.SIGABRT,
        os.Interrupt)

    go func() {
        sig := <-signals
        fmt.Printf("\nReceived signal: %v\n", sig)
        cleanup()
        os.Exit(0)
    }()

    logrus.Infof("Press Ctrl+C or close terminal to trigger cleanup.")
    select {} // 阻塞主goroutine,直到信号触发
}

func cleanup() {
    fmt.Println("Performing cleanup tasks...")
    touchFile := exec.Command("touch", "testfile.txt")
    _, err := touchFile.CombinedOutput()

    if err != nil {
        fmt.Printf("Cleanup error: %v\n", err)
    }
}

关键说明

  • syscall.Setpgid(0,0):确保程序成为进程组组长,终端关闭时信号能直接传递
  • 扩展信号监听:覆盖iTerm等终端可能发送的非标准信号
  • 直接执行清理:避免通道传递的潜在阻塞问题

内容的提问来源于stack exchange,提问作者gurpsone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:45:40