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);
}




