go-run meets pstree
以下の内容のメモ
- 現在の環境1で実行されるprocessの概観を掴む方法
- go runがtmp directoryにbuildした結果のバイナリをsubprocessとして実行していること
go runが実行するprocess
例えば以下の様なテキトウなfile serverのようなコードがあるとする2。
main.go
package main import ( "log" "net/http" ) func main() { log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc")))) }
これをgo runで実行する。
$ go run main.go
/usr/share/doc
以下を公開しているので以下の様な形でgoのtos.htmlが取れる。
$ http -b :8080/go/tos.html <!--{ "Title": "Terms of service" }--> <p> The Go website (the "Website") is hosted by Google. By using and/or visiting the Website, you consent to be bound by Google's general <a href="//www.google.com/intl/en/policies/terms/">Terms of Service</a> and Google's general <a href="//www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>. </p>
まぁ今回の記事の主題はそこではない。とりあえず今回はgo runで実行されたprocessがあるというところが重要。
実行されるprocessの概観を掴む
実行した環境で Ctrl + z をして実行中のプロセスをバックグラウンドに持っていく。そしてこの環境で実行されているprocessを知りたい。
# ctrl + z [1]+ Stopped go run main.go
ここで ps --forest
が便利。個人的には初手 ps -j --forest
が一番わかり易いと思った(linux環境)。
$ ps -j --forest PID PGID SID TTY TIME CMD 9349 9349 9349 pts/2 00:00:00 bash 8612 8612 9349 pts/2 00:00:00 \_ go 8665 8612 9349 pts/2 00:00:00 | \_ main 8795 8795 9349 pts/2 00:00:00 \_ ps
なるほどたしかにgo runはmain.goのprocessを生成している。そんなわけでgo runで立ち上げたprocessをCtrl cなどで終わらせようとした時にもたつくような感じがあったりするわけっぽい(体感的なものかもだけど)。ちなみにたまたまこの記事を書く時に調べて --forest
オプションの存在を知った。ps奥が深い。
ここでpid(process id)の他にpgid(process group id)の方に注目、go run側のprocessを殺してもそのprocessが生成しているsub processまで殺せない場合があるかもしれない、幸いpgidは一緒なので一気にprocessをkillしたい場合にはpgidを指定して実行してあげるのが良さそう。
$ pkill -TERM -g 8612 $ fg go run main.go (wd: ~/venvs/my/individual-sandbox/daily/20190806/example_go/03fileserver) Terminated
ところでこのページを読んでkillでもpgidを指定できることを知った(それとは関係なくプロセスのことをあまり知らないひとの勉強にこのweb book(?)良さそうだなーと想ったりした)。
たしかにmanにも書いてある。kill -<pgid>
でできたんだ。今までpkillを使っていた。
$ man kill ... -n where n is larger than 1. All processes in process group n are signaled. When an argument of the form '-n' is given, and it is meant to denote a process group, either a signal must be specified first, or the argument must be preceded by a '--' option, otherwise it will be taken as the signal to send.
pstree, pkill
ところでps, killだけでできそうならpstree, pkill不要じゃんとかおもったりもしたんだけれど。イメージ的にこういう認識。
- ps, kill -- よりprimitive。詳細が異なる
- pstree, pkill, (pgrep) -- より抽象度が高め。便利(?)
どうやらmacの方のpsには--forest
オプションの存在が無いらしい。そういう意味ではpstreeでの確認方法も知っておくと良いかもしれない。デフォルトではスレッド自体も表示してしまって邪魔なので-Tオプションで無視するようにしている。pid,pgidは知りたいですよね。
# 実行しなおしたのでpidなどが変わっている $ ps aux | ioknife rest | grep "go run" USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND me 9561 0.2 0.1 780048 20996 pts/1 Sl+ 16:19 0:00 go run main.go $ pstree -T -g -p 9561 go(9561,9561)───main(9617,9561)
ちなみに逆方向も実行できて祖先方向も見れる(-s)。
$ pstree -T -g -p -s 9561 systemd(1,1)───systemd(1475,1475)───gnome-terminal-(1855,1855)───bash(1883,1883)───screen(2403+
psの--forest
オプションを含めて記事などを書くときに便利だと想う。
clientがrequestしてそれが終了したらserverを終了させたい
実は元々の裏テーマとして以下の様なことがしたかった。
- serverを立ち上げる (e.g. 冒頭のファイルサーバー)
- clientを立ち上げる (e.g. curl, httpieなどでのrequest)
- clientのrequestが終了したらserverを終了させたい。
これのためにioknifeにtooコマンドを作っていたりしたのだけれど。
$ ioknife too --cmd <server> --cmd <client> Ctrl+c でSIGINT
まぁmakefileとかでも自由にやりたいよね。ということで色々考えたりしていたのだった。暫定的には以下の様なものなのだけれど。まぁterminated的なメッセージがエラー扱いなので微妙だなーと思ったりした。
default: ioknife too --cmd "go run main.go" --cmd "make client"& echo $$! > x.pid && wait $$(cat x.pid) client: sleep 1 http GET :8080/go/tos.html pkill -TERM -g $$(ps -o pgid -p $$(cat x.pid) | ioknife rest) || echo ok
こんな感じ。
$ make ioknife too --cmd "go run main.go" --cmd "make client"& echo $! > x.pid && wait $(cat x.pid) [1] make make[1]: Entering directory /$HOME/venvs/my/individual-sandbox/daily/20190806/example_go/03fileserver' [1] make sleep 1 [1] make http GET :8080/go/tos.html [1] make <!--{ [1] make "Title": "Terms of service" [1] make }--> [1] make [1] make <p> [1] make The Go website (the "Website") is hosted by Google. [1] make By using and/or visiting the Website, you consent to be bound by Google's general [1] make <a href="//www.google.com/intl/en/policies/terms/">Terms of Service</a> [1] make and Google's general [1] make <a href="//www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>. [1] make </p> [1] make pkill -TERM -g $(ps -o pgid -p $(cat x.pid) | ioknife rest) || echo ok [1] make PGID INFO:ioknife.signalhandle:send signal (Signals.SIGTERM) make: *** [Makefile:2: default] Terminated Terminated
さいごに
そんなわけでioknifeのtooにSIGHUPあたりを送ったら全体にSIGTERMを送るみたいな機能を付けても良いかもなーとおもったりした3。まぁ再帰的には使えないけれど。
-
ここではログインしているシェルの意味↩
-
godocのexampleにあるようなテキトウなコード↩
-
全ての立ち上げたsub processがSIGHUPでのgraceful stopに対応しているということは期待できないのでSIGTERM↩