概要
os.Exit()を実行した際、プログラムが即座に終了するため、defer された関数が呼ばれない。
これについて整理します。
os.Exit()
ドキュメントを確認すると、defer された関数が呼ばれないことも明記されている
Exit causes the current program to exit with the given status code. Conventionally, code zero indicates success, non-zero an error. The program terminates immediately; deferred functions are not run.
For portability, the status code should be in the range [0, 125].
os.Exitの内部処理としては、システムコール exitを発行することにより、プログラムを終了させている。
- os.Exitでは
// Exit causes the current program to exit with the given status code. // Conventionally, code zero indicates success, non-zero an error. // The program terminates immediately; deferred functions are not run. // // For portability, the status code should be in the range [0, 125]. func Exit(code int) { if code == 0 && testlog.PanicOnExit0() { // We were told to panic on calls to os.Exit(0). // This is used to fail tests that make an early // unexpected call to os.Exit(0). panic("unexpected call to os.Exit(0) during test") } // Inform the runtime that os.Exit is being called. If -race is // enabled, this will give race detector a chance to fail the // program (racy programs do not have the right to finish // successfully). If coverage is enabled, then this call will // enable us to write out a coverage data file. runtime_beforeExit(code) syscall.Exit(code) }
- 最終的にはruntimeパッケージのsyscall_exit()により、exitシステムコールが呼ばれる
https://github.com/golang/go/blob/master/src/runtime/runtime.go#L63-L67
func syscall_Exit(code int) { exit(int32(code)) }
log.Fatal時挙動について
os.Exitを内部で読んでいるlog.Fatal系の処理についてはどうでしょうか。
log.Fatalでは内部でos.Exitが呼ばれ、ログ出力と合わせてプログラムが終了します。
os.Exit()が呼ばれるため、上述した通り、deferで指定された関数は呼ばれずにプログラムが終了するので注意です。
https://cs.opensource.google/go/go/+/refs/tags/go1.21.2:src/log/log.go;l=284