#author("2017-10-22T11:44:10+09:00","default:afuruta","afuruta")
#author("2017-11-04T02:40:37+09:00","default:afuruta","afuruta")
* なぜ file_operations の open の対は release なの? [#f07b3992]
** file_operations 構造体 [#n557dd82]
&ogdefs(file_operations); 構造体は system call の界面が受けた要求をデバイスドライバに渡すために使われる構造体です。この構造体のメンバの殆どは関数を指すポインタで構成されています。次のコード片は &ogdefs(file_operations); 構造体です。
#code(c,/struct.*file_operations[[:space:]]*{/../^};$/,ogfileone:/include/linux/fs.h);
#textbox(thought,VFS 周辺の file_operations 構造体の使われ方の方が面白いかも...){{
VFS(Virtual File System) から各種 FS(File System) の処理関数を呼び出す所でも使わています。ここでは VFS と各種 FS 廻りには触れません。
}}
メンバ名は system call の関数名とほぼ一致しています。メンバ名と処理する内容も system call の名前のとおりに一致しています。メンバの中で open の対になるはずの close が見つかりません。それらしいメンバは release です。ここではなぜ release なのか背景を探ることにします。
** release の特徴 - デバイスドライバ側で意識しておく必要があること [#u2636017]
先に &ogdefs(file_operations); の release メソッドの特徴を挙げます。次の通りです。
- user space の application が close() を使った時に呼ばれるとは限りません。参照を減らし(&ogdefs(fput());)、参照されなくなった時に呼び出されます。参照を増やす system call で主にデバイス・ドライバに関係する API は dup(), dup2() (これに伴う処理は &ogrefs(get_file(),get_file,file.c);)、poll() (これに伴う処理は &ogrefs(get_file(),get_file,select.c);、mmap() (これに伴う処理は &ogrefs(get_file(),get_file,mm);) です。これらの対になる system call または segmentation fault などにより強制終了する状況で参照カウンタは減ります。その他にも &ogdefs(fget());, &ogdefs(__fget());, &ogdefs(fget_raw());, &ogdefs(get_file(),get_file,fs.h);, &ogdefs(get_file_rcu()); を呼び出している所で参照を増やしています。
- user space の application が close() を使った時に呼ばれるとは限りません。参照を減らし(&ogdefs(fput());)、参照されなくなった時に呼び出されます。参照を増やす system call で主にデバイス・ドライバに関係する API は dup(), dup2() (これに伴う処理は &ogrefs(get_file(),get_file,file.c);)、poll() (これに伴う処理は &ogrefs(get_file(),get_file,select.c);)、mmap() (これに伴う処理は &ogrefs(get_file(),get_file,mm);) です。これらの対になる system call または segmentation fault などにより強制終了する状況で参照カウンタは減ります。その他にも &ogdefs(fget());, &ogdefs(__fget());, &ogdefs(fget_raw());, &ogdefs(get_file(),get_file,fs.h);, &ogdefs(get_file_rcu()); を呼び出している所で参照を増やしています。
- task work または delayed work (&ogsearch(delayed_fput_work);)から呼び出されます。task work からの呼び出しは x86(i686 など), x86_64 アーキテクチャでは system call 割り込みの後半処理(&ogsearch(call do_notify_resume from entry_32.S,do_notify_resume,entry_32.S);,&ogsearch(call do_notify_resume from entry_64.S,int_signal,entry_64.S);)から呼び出しになります。
** close 処理を追跡する [#i62a92e0]
kernel の中で close() の処理を追跡する適当な関数として &ogdefs(filp_close()); を見てみます。ここから &ogdefs(file_operations); 構造体の release が呼ばれるところまで追跡します。先に &ogdefs(filp_close()); から user space 側の実装(close()の実装側)に辿っておきましょう。&ogdefs(__close_fd());, &ogsearch(SYSCALL_DEFINE1 close,__close_fd,open.c); となります。
#textbox(note,kernel 内でファイルを扱う filp_xxx()){{
&ogdefs(filp_close()); が有るということは &ogdefs(filp_open()); も有ります。これらの関数を使用している周辺コードを読むと、kernel 内から file を扱う方法が分かります。
}}
#code(c,/filp_close/../^}$/,begin-=4,ogfileone:/fs/open.c);
&ogdefs(filp_close()); の中に &ogdefs(fput()); を呼び出すところが見つかります。&ogdefs(fput()); ではファイルの参照カウントを減じて 0 になったら、ファイルを開いた後に確保して使用していたリソースを開放する処理を始めます。殆どの場合 &ogdefs(init_task_work());,  &ogdefs(task_work_add()); で &ogdefs(____fput());, (&ogdefs(____fput()); の中から &ogdefs(__fput());) を呼び出す処理を task work としてリンクリストに連結します。&ogdefs(task_work_add()); の最後の引数が true となっているので &ogdefs(task_work_add()); のなかで &ogdefs(set_notify_resume()); を呼び出します。いくつかの関数をネストした先で &ogdefs(thread_info); 構造体の flag に &ogdefs(TIF_NOTIFY_RESUME); をセットします。system call の後半処理で task work が実行されるようになります。

先に触れたように x86 では &ogsearch(call do_notify_resume from entry_32.S,do_notify_resume,entry_32.S);、x86_64 では &ogsearch(call do_notify_resume from entry_64.S,int_signal,entry_64.S); の部分が system call が後半処理部分です。&ogdefs(do_notify_resume()); のなかから &ogdefs(tracehook_notify_resume()); 呼び出し、さらに &ogdefs(task_work_run()); を呼び出して task work に連結された関数を実行しています。途中 &ogdefs(TIF_NOTIFY_RESUME); が呼び出し条件として判定されています。

&ogdefs(__fput()); を追跡します。&ogdefs(__fput()); のなかで f_op->fasync() と f_op->release() を呼び出す箇所が見つかります。fasync() の方は何かの事象で signal を発生する (kill() 相当) 処理が実装されている場合、それを止める様にデバイス・ドライバを呼び出します。続けて release() を呼び出しています。release() は close() を呼んだ時に呼び出されるのではなく、「参照されなくなった」時に呼ばれることが分かります。
#code(c,/void[[:space:]]*__fput/../^}$/,ogfileone:/fs/file_table.c);

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS