Process golang重新启动的父进程不';不接收信号

Process golang重新启动的父进程不';不接收信号,process,go,child-process,kill-process,sigint,Process,Go,Child Process,Kill Process,Sigint,我正在写一个小程序来管理其他进程的重启 基本上,当一个应用程序进程启动时(称之为A),它会生成一个新进程(称之为D),该进程有一个简单的HTTP服务器。当D接收到http请求时,它将终止A并重新启动它 问题是,A现在不响应CTRL-C,我不知道为什么。可能是一些简单的事情,也可能是我不太理解进程、终端和信号之间的关系。但是它运行在同一个终端上,使用相同的stdin/stdout/stderr。下面是演示这种行为的完整程序 package main import ( "flag"

我正在写一个小程序来管理其他进程的重启

基本上,当一个应用程序进程启动时(称之为A),它会生成一个新进程(称之为D),该进程有一个简单的HTTP服务器。当D接收到http请求时,它将终止A并重新启动它

问题是,A现在不响应CTRL-C,我不知道为什么。可能是一些简单的事情,也可能是我不太理解进程、终端和信号之间的关系。但是它运行在同一个终端上,使用相同的stdin/stdout/stderr。下面是演示这种行为的完整程序

package main

import (
    "flag"
    "log"
    "net/http"
    "os"
    "os/exec"
    "strconv"
    "time"
)

/*
    Running this program starts an app (repeatdly prints 'hi') and spawns a new process running a simple HTTP server
    When the server receives a request, it kills the other process and restarts it.
    All three processes use the same stdin/stdout/stderr.
    The restarted process does not respond to CTRL-C :(
*/

var serv = flag.Bool("serv", false, "run server")

// run the app or run the server
func main() {
    flag.Parse()
    if *serv {
        runServer()
    } else {
        runApp()
    }
}

// handle request to server
// url should contain pid of process to restart
func handler(w http.ResponseWriter, r *http.Request) {
    pid, err := strconv.Atoi(r.URL.Path[1:])
    if err != nil {
        log.Println("send a number...")
    }
    // find the process
    proc, err := os.FindProcess(pid)
    if err != nil {
        log.Println("can't find proc", pid)
        return
    }
    // terminate the process
    log.Println("Terminating the process...")
    err = proc.Signal(os.Interrupt)
    if err != nil {
        log.Println("failed to signal interupt")
        return
    }
    // restart the process
    cmd := exec.Command("restarter")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Start(); err != nil {
        log.Println("Failed to restart app")
        return
    }
    log.Println("Process restarted")
}

// run the server.
// this will only work the first time and that's fine
func runServer() {
    http.HandleFunc("/", handler)
    if err := http.ListenAndServe(":9999", nil); err != nil {
        log.Println(err)
    }
}

// the app prints 'hi' in a loop
// but first it spawns a child process which runs the server
func runApp() {
    cmd := exec.Command("restarter", "-serv")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Start(); err != nil {
        log.Println(err)
    }

    log.Println("This is my process. It goes like this")
    log.Println("PID:", os.Getpid())
    for {
        time.Sleep(time.Second)
        log.Println("hi again")
    }
}
该程序预计将被安装。为方便起见,您可以使用
获取github.com/ebuchman/restarter


使用
重启器运行程序。它应该打印它的进程id。然后
curlhttp://localhost:9999/
启动重新启动。新流程现在不会响应CTRL-C。为什么?我遗漏了什么?

您可以查看两个http服务器框架采用的监听和截获信号的方法(包括
SIGINT
,甚至
SIGTERM

  • ,其中使用通道:

    // catch signals:
    signals := make(chan os.Signal)
    signal.Notify(signals, os.Interrupt, os.Kill)
    
  • ,其中使用了类似的方法:

    var stdSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
    var sigchan = make(chan os.Signal, 1)
    func init() {
        go waitForSignal()
    }
    

    • 这与围棋没有任何关系。从终端外壳启动进程A。进程A启动进程D(不确定B发生了什么,但没关系)。进程D终止进程A。现在您的shell看到它启动的进程已退出,因此shell准备侦听另一个命令。进程D启动进程A的另一个副本,但shell对此一无所知。键入^C时,shell将处理它。如果运行另一个程序,shell将安排^C转到该程序。shell对进程A的副本一无所知,因此它永远不会将^C指向该进程。

      不幸的是,这没有帮助。问题不在于进程没有信号处理程序,而在于信号根本无法到达进程,因为它处于被其子进程重新启动的奇怪状态。就像终端失去了对进程的钩子,但是pgid没有改变。是的。我试图了解,即使父进程组已死亡,是否有可能将终端连接到进程组。不是。因此,解决方案是先启动A',然后让D终止并重新启动A',然后让A阻塞,这样终端就可以向整个组发送信号