在 Golang 中,如何使用 SIGTERM 而不是 SIGKILL 终止 os.exec.Cmd 进程?

问题描述

目前,我正在使用 Golang os.exec.Cmd.Process.Kill() 方法(在 Ubuntu 机器上)终止进程。

这似乎是立即终止进程,而不是优雅地终止。我启动的一些进程也会写入文件,这会导致文件被截断。

我想使用 Golang 使用 SIGTERM 而不是 SIGKILL 优雅地终止进程。

这是一个使用 cmd.Process.Kill() 启动然后终止的进程的简单示例,我想要 Golang 中使用 SIGTERM 而不是 SIGKILL 的 Kill() 方法的替代方法,谢谢!>

import "os/exec"

cmd := exec.Command("nc","example.com","80")
if err := cmd.Start(); err != nil {
   log.Print(err)
}
go func() {
  cmd.Wait()
}()
// Kill the process - this seems to kill the process ungracefully
cmd.Process.Kill()

解决方法

您可以使用 Signal() API。支持的 Syscalls 在这里。

所以基本上你可能想要使用

cmd.Process.Signal(syscall.SIGTERM)

另外请注意按照文档。

保证出现在 os 包中的唯一信号值 所有系统都是 os.Interrupt(向进程发送中断)和 os.Kill(强制进程退出)。在 Windows 上,发送 os.Interrupt 没有实现带有 os.Process.Signal 的进程;它会回来 错误而不是发送信号。

,
cmd.Process.Signal(syscall.SIGTERM)
,

您可以使用:

cmd.Process.Signal(os.Interrupt)

测试示例:

package main

import (
    "fmt"
    "log"
    "net"
    "os"
    "os/exec"
    "sync"
    "time"
)

func main() {
    cmd := exec.Command("nc","-l","8080")
    cmd.Stderr = os.Stderr
    cmd.Stdout = os.Stdout
    cmd.Stdin = os.Stdin
    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        err := cmd.Wait()
        if err != nil {
            fmt.Println("cmd.Wait:",err)
        }
        fmt.Println("done")
        wg.Done()
    }()

    fmt.Println("TCP Dial")
    fmt.Println("Pid =",cmd.Process.Pid)
    time.Sleep(200 * time.Millisecond)
    // or comment this and use: nc 127.0.0.1 8080
    w1,err := net.DialTimeout("tcp","127.0.0.1:8080",1*time.Second)
    if err != nil {
        log.Fatal("tcp DialTimeout:",err)
    }
    defer w1.Close()

    fmt.Fprintln(w1,"Hi")
    time.Sleep(1 * time.Second)
    // cmd.Process.Kill()
    cmd.Process.Signal(os.Interrupt)
    wg.Wait()
}

输出:

TCP Dial
Pid = 21257
Hi
cmd.Wait: signal: interrupt
done