golang で子プロセス実行するときに、Backgroundにする(やり方が分からず・・・
Goで書いてる運用系のツールで、以下のようなことをやりたかった。
- メインのツールと、サブツールみたいなのがある。
- サブツールはバックグラウンドで起動しておいて、メインのツールから利用する軽いWebAPI
- メインのツールはある程度起動/停止を繰り返す感じになる
- メインのツール起動時にサブツール(API)が起動してなかったら、バックグラウンドで起動したい
つまり、メインのツール(M)起動時に、サブのツール(S)が起動してなかったらバックグラウンド起動しておく、ということをしたい。
プロセスの存在チェック
プロセス調べるのは、Goの標準ライブラリで無さそうなので pgrep "S"
コマンドを実行して、ExitCodeで判定
package main import ( "bytes" "fmt" "os/exec" "strings" "syscall" ) func main() { process_name := "target_tool_name" cmd := exec.Command("pgrep", process_name) cmd.Stdin = strings.NewReader("") var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { e := err.(*exec.ExitError) s := e.Sys().(syscall.WaitStatus) status := s.ExitStatus() if status == 1 { // exit(1) は見つからない時 fmt.Printf("process '%s' is found") } else { fmt.Println(err) // なんかその他のエラー } } else { // プロセス1つでも見つかるとエラーにならない fmt.Printf("process '%s' is found", process_name) } }
こんな感じ。無駄もあるかもしれないけど Linux ならこで良さそう?
ExitCode 取るのが思ったより面倒だった。
プロセス実行しつつ、バックグラウンドにする
os/exec
の Command
では、 Start()
/ Run()
辺りでプロセスを実行する。
Run()
だと終了を待つけど、 Start()
は goroutine で実行して待たない。
Wait()
使うと、プロセスの終了を待つ感じになると思う。
で、 Start()
を使うとバックグラウンド実行かな、と思いきや、
自身のプロセスが終了するタイミングで Start()
で実行したプロセスも終わってしまう。
(Ctrl-C で終わる場合)
このサイト見ると、プロセスグループが同じだから(?)みたいなことが書いてあって、
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
で分離できそうなことが書いてある。
(と読んで思った)
けど、できなかった。
結局、 /path/to/sub/tool &
でバックグラウンド実行するように shellscript を作っておいて、
その shellscript を golang から実行するようにした。
Goのメインツールのプロセスとは分離できたけど、イマイチな感じになった。
感想
もうちょっとスマートな方法ありそう。 勉強が必要そう。。。。
(追記)そう言えば supervisord
経由で起動するがどうの、という感じのことが書いてあった。