2015-02-07 22 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