2015-02-07 24 views
2

我正在编写一个小程序来管理重启到其他进程。golang重启父进程没有收到SIGINT

基本上,当一个应用程序进程启动(称为A)时,它会产生一个新的进程(称为D),它具有一个简单的HTTP服务器。当D接收到一个http请求时,它杀死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") 
    } 
} 

该程序预计将被安装。为方便起见,您可以使用go get github.com/ebuchman/restarter来获取。

使用restarter运行该程序。它应该打印它的进程ID。然后curl http://localhost:9999/<procid>启动重启。新进程现在不会响应CTRL-C。为什么?我错过了什么?

回答

0

您可以检查出通过两个HTTP服务器框架以听和截距信号所采取的做法(包括SIGINT,甚至SIGTERM

+0

不幸的是,这并没有帮助。问题不在于该过程没有处理信号的处理程序,而是信号根本没有进入该过程,因为它处于由其孩子重新启动的奇怪状态。这就像终端已经失去了过程的钩子,但pgid的没有改变。 – Ethan 2015-02-08 20:35:41

1

这和Go没什么关系。您从终端外壳开始处理A.进程A启动进程D(不知道B发生了什么,但没关系)。进程D终止进程A.现在你的shell看到它启动的进程已经退出,所以shell准备听取另一个命令。进程D启动进程A的另一个副本,但shell不知道任何关于它的内容。当你输入^ C时,shell会处理它。如果你运行另一个程序,shell将安排这样^ C去那个程序。 shell对进程A的拷贝一无所知,所以它永远不会将^ C指向那个进程。

+0

是的。我试图了解,即使父母死亡,是否有可能让终端挂在进程组上。不是。所以解决方案有A开始A',让D杀死并重新启动A',并让A阻止,这样终端可以发信号通知整个组 – Ethan 2015-02-10 00:14:22