anone_inode(匿名inode)
Rev.3を表示中。最新版はこちら。
/tmp/file1は/tmpディレクトリから削除され、パスでの参照はできませんが、ファイル自体は存在し、a.outはfd1で参照し続けることが可能です。このようなファイルのinodeをanone inode(匿名inode)と言うようです。#include <fcntl.h> #include <stdio.h> int main(void) { int err; int fd1 = open( "/tmp/file1", O_CREAT | O_RDWR, 0666 ); int fd2 = open( "/tmp/file2", O_CREAT | O_RDWR, 0666 ); err = unlink( "/tmp/file1" ); printf("err:%d\n", err); while(1) sleep(1); } [root@localhost lkm]# ./a.out & [1] 17342 err:0 [root@localhost lkm]# ls /tmp/file* /tmp/file2 [root@localhost lkm]# ls -l /proc/17342/fd 合計 0 lrwx------ 1 root root 64 3月 7 15:29 0 -> /dev/pts/2 lrwx------ 1 root root 64 3月 7 15:29 1 -> /dev/pts/2 lrwx------ 1 root root 64 3月 7 15:29 2 -> /dev/pts/2 lrwx------ 1 root root 64 3月 7 15:29 3 -> /tmp/file1 (deleted) lrwx------ 1 root root 64 3月 7 15:29 4 -> /tmp/file2システムコールunlinkは、AT_FDCWDでdo_unlinkat()をコールします。AT_FDCWDはpathnameが相対パスの時プロセスのカレントパス(current->fs->pwd)から検索します。
SYSCALL_DEFINE1(unlink, const char __user *, pathname) { return do_unlinkat(AT_FDCWD, pathname); }mnt_want_write()でunlinkするファイルのファイルシステムが書き込み可能ならmnt->mnt_writers++とし、そうでないなら削除できません。lookup_hash()でdentry取得のためdentry->d_count++すたのを、dput(dentry)でdentry->d_count--としてエラーです。
ファイルシステムが書き込み可能なら、vfs_unlink()で実ファイルおよびそのキャッシュの削除を行い、iput()でinode->i_count--とします。inode->i_count=0ならinodeキャッシュの削除も行いますが、そうでないならinodeキャッシュは存在することになり、anone inode(匿名inode)の誕生となります。
static long do_unlinkat(int dfd, const char __user *pathname) { int error; char *name; struct dentry *dentry; struct nameidata nd; struct inode *inode = NULL; error = user_path_parent(dfd, pathname, &nd, &name); if (error) return error; error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; nd.flags &= ~LOOKUP_PARENT; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { if (nd.last.name[nd.last.len]) goto slashes; inode = dentry->d_inode; if (!inode) goto slashes; ihold(inode); error = mnt_want_write(nd.path.mnt); if (error) goto exit2; error = security_path_unlink(&nd.path, dentry); if (error) goto exit3; error = vfs_unlink(nd.path.dentry->d_inode, dentry); exit3: mnt_drop_write(nd.path.mnt); exit2: dput(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); if (inode) iput(inode); /* truncate the inode here */ exit1: path_put(&nd.path); putname(name); return error; slashes: error = !dentry->d_inode ? -ENOENT : S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; goto exit2; }削除するファイルのinodeコールバックのunlink()がコールされます。これはディレクトリのinodeのデータブロックから、このinodeを管理している設定を削除し、d_delete()でそのキャッシュも削除され、ファイルは階層的に管理されなくなりました。ただしunlink()コールバック関数は対象ファイルのinodeブロックは削除しません。
int vfs_unlink(struct inode *dir, struct dentry *dentry) { int error = may_delete(dir, dentry, 0); if (error) return error; if (!dir->i_op->unlink) return -EPERM; mutex_lock(&dentry->d_inode->i_mutex); if (d_mountpoint(dentry)) error = -EBUSY; else { error = security_inode_unlink(dir, dentry); if (!error) { error = dir->i_op->unlink(dir, dentry); if (!error) dont_mount(dentry); } } mutex_unlock(&dentry->d_inode->i_mutex); if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { fsnotify_link_count(dentry->d_inode); d_delete(dentry); } return error; }
追記
inodeブロックが削除されるのは、iput()でinode->i_count=0の時、iput_final()をコールし、スーパブロックオペレーションdrop_inode()コールする事でおこなっています。サンプルの場合/tmp/file1のopen時、inode->i_count++となっているため、iput_final()がコールされません。void iput(struct inode *inode) { if (inode) { BUG_ON(inode->i_state & I_CLEAR); if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) iput_final(inode); } } static void iput_final(struct inode *inode) { struct super_block *sb = inode->i_sb; const struct super_operations *op = inode->i_sb->s_op; int drop; WARN_ON(inode->i_state & I_NEW); if (op->drop_inode) drop = op->drop_inode(inode); else drop = generic_drop_inode(inode); if (!drop && (sb->s_flags & MS_ACTIVE)) { inode->i_state |= I_REFERENCED; if (!(inode->i_state & (I_DIRTY|I_SYNC))) inode_lru_list_add(inode); spin_unlock(&inode->i_lock); return; } if (!drop) { inode->i_state |= I_WILL_FREE; spin_unlock(&inode->i_lock); write_inode_now(inode, 1); spin_lock(&inode->i_lock); WARN_ON(inode->i_state & I_NEW); inode->i_state &= ~I_WILL_FREE; } inode->i_state |= I_FREEING; if (!list_empty(&inode->i_lru)) inode_lru_list_del(inode); spin_unlock(&inode->i_lock); evict(inode); }