なぜ file_operations の open の対は release なの? †file_operations 構造体 †file_operations 構造体は system call の界面が受けた要求をデバイスドライバに渡すために使われる構造体です。この構造体のメンバの殆どは関数を指すポインタで構成されています。次のコード片は file_operations 構造体です。
|
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
| - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ! |
|
VFS 周辺の file_operations 構造体の使われ方の方が面白いかも...
VFS(Virtual File System) から各種 FS(File System) の処理関数を呼び出す所でも使わています。ここでは VFS と各種 FS 廻りには触れません。
メンバ名は system call の関数名とほぼ一致しています。メンバ名と処理する内容も system call の名前のとおりに一致しています。メンバの中で open の対になるはずの close が見つかりません。それらしいメンバは release です。ここではなぜ release なのか背景を探ることにします。
先に file_operations の release メソッドの特徴を挙げます。次の通りです。
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 を扱う方法が分かります。
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | - | | ! - | | - | | ! | | | | - | | ! | | ! |
|
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() を呼んだ時に呼び出されるのではなく、「参照されなくなった」時に呼ばれることが分かります。
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
| - | | | | | | | - | | ! | | | - | | ! | | | | | - | ! | | | | - | | ! | | | | | | ! |
|