なぜ file_operations の open の対は release なの? †file_operations 構造体 †file_operations 構造体は system call の界面が受けた要求をデバイスドライバに渡すために使われる構造体です。この構造体のメンバの殆どは関数を指すポインタで構成されています。次のコード片は file_operations 構造体です。 OpenGrok(linux-4.1.27/include/linux/fs.h:1589)
VFS 周辺の file_operations 構造体の使われ方の方が面白いかも... VFS(Virtual File System) から各種 FS(File System) の処理関数を呼び出す所でも使わています。ここでは VFS と各種 FS 廻りには触れません。 メンバ名は system call の関数名とほぼ一致しています。メンバ名と処理する内容も system call の名前のとおりに一致しています。メンバの中で open の対になるはずの close が見つかりません。それらしいメンバは release です。ここではなぜ release なのか背景を探ることにします。 release の特徴 - デバイスドライバ側で意識しておく必要があること †先に file_operations の release メソッドの特徴を挙げます。次の通りです。
close 処理を追跡する †kernel の中で close() の処理を追跡する適当な関数として filp_close() を見てみます。ここから file_operations 構造体の release が呼ばれるところまで追跡します。先に filp_close() から user space 側の実装(close()の実装側)に辿っておきましょう。__close_fd(), SYSCALL_DEFINE1 close となります。 kernel 内でファイルを扱う filp_xxx() filp_close() が有るということは filp_open() も有ります。これらの関数を使用している周辺コードを読むと、kernel 内から file を扱う方法が分かります。 OpenGrok(linux-4.1.27/fs/open.c:1058)
filp_close() の中に fput() を呼び出すところが見つかります。fput() ではファイルの参照カウントを減じて 0 になったら、ファイルを開いた後に確保して使用していたリソースを開放する処理を始めます。殆どの場合 init_task_work(), task_work_add() で ____fput(), (____fput() の中から __fput()) を呼び出す処理を task work としてリンクリストに連結します。task_work_add() の最後の引数が true となっているので task_work_add() のなかで set_notify_resume() を呼び出します。いくつかの関数をネストした先で thread_info 構造体の flag に TIF_NOTIFY_RESUME をセットします。system call の後半処理で task work が実行されるようになります。 先に触れたように x86 では call do_notify_resume from entry_32.S、x86_64 では call do_notify_resume from entry_64.S の部分が system call が後半処理部分です。do_notify_resume() のなかから tracehook_notify_resume() 呼び出し、さらに task_work_run() を呼び出して task work に連結された関数を実行しています。途中 TIF_NOTIFY_RESUME が呼び出し条件として判定されています。 __fput() を追跡します。__fput() のなかで f_op->fasync() と f_op->release() を呼び出す箇所が見つかります。fasync() の方は何かの事象で signal を発生する (kill() 相当) 処理が実装されている場合、それを止める様にデバイス・ドライバを呼び出します。続けて release() を呼び出しています。release() は close() を呼んだ時に呼び出されるのではなく、「参照されなくなった」時に呼ばれることが分かります。 OpenGrok(linux-4.1.27/fs/file_table.c:186)
|