2011年02月17日
exitと_exitの違い/tmpfileの削除のしくみ
Linuxでのプロセスの終了処理について少し調べました。
今まであまり意識していなかったexitと_exitの違いも理解できました。
昔C言語で小さなマイコンのプログラムをしていた頃は、一度main関数を実行すると、そこから抜けることはなかったので、main関数を抜けた後のことはあまり意識することはありませんでした。
しかし、Linuxなどプロセス単位で動かす場合には、main関数を抜けた後の終了処理も理解しておく必要があると思います。
main関数とexitの関係
main関数はスタートアップルーチンから呼び出されますが、mainから戻った後にmain関数の戻り値を引数としてexitが呼び出されます。
つまり、
exit(main(argc, argv, env));
(ちなみに、mainという名前の関数はCコンパイラで特別扱いされていて、return文が無い場合にはコンパイラによって自動的に return 0; が付加されます。)
プログラムの中から直接exitを呼び出して終了させることもできます。
exitは呼び出したら、そこから戻ってくることはありません。
exitと_exitの関係
簡単に言うと、_exitはすぐにシステムコールを呼んでそのプロセスをその場で終了させます。exitはatexitで登録された関数を全て呼び出して、その後に_exitを呼び出します。
C++のグローバルクラスやスタティッククラスのデストラクタはexitの中で呼び出されます。
プログラムの中でexitで終了させているところを代わりに_exitを呼ぶとこれらの終了処理が実行されないことになります。
_exitの実際のソース
UbuntuのeglibcとAndroidのbionicのソースを調べたのですが、bionicの方がより単刀直入にシステムコールを呼び出していました。
_exit: .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__NR_exit_group swi #0 ldmfd sp!, {r4, r7} movs r0, r0 bxpl lr b __set_syscall_errno .fnend
exitでなくexit_groupというシステムコールを呼び出しています。exit_groupはプロセスのThread groupの全てのスレッドを終了させます。カーネルのソースを見ると、do_group_exitは最後にdo_exitを呼んでいます。
ファイルのクローズはどこで行われる?
Linuxの場合はカーネルの中で行われます。
kernel/exit.c: close_file
そのプロセスで開いていたファイルが全てクローズされることは保証されています。
tmpfileの削除はどこで行われる?
exitをmanコマンドで調べると、標準ライブラリのtmpfile関数で作成したファイルは削除されると書いてあります。
しかし、標準ライブラリのtmpfileのソースを見ても終了時にそのファイルを削除するための関数をatexitで登録しているところが見当たりません。
さらにtmpfileのソースを調べてみると、ランダムなファイル名でファイルをopenしてfdを取得した後に、そのファイルをunlinkで削除していました。この場合、そのfdをcloseした時に実際にそのファイルが削除されます。前述の通り、プロセス終了時にはカーネルが必ずクローズしてくれるので、これをもってtmpfileは削除されるわけです。
unlinkを呼ぶにはファイル名が必要ですが、これだとファイル名をどこかに覚えておく必要もありません。うまくできていると思いました。