無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

anone_inode(匿名inode)


/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++とし、do_unlinkat()からコールされる時ihold(inode)でinode->i_count++としています。従ってinode->i_count=2となっているため、inode->i_count--で1となるため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);
}

補足

ファイルを削除するというのは、ファイル自体のinodeにかかるデータを削除する事と、親ディレクトリinodeのデータブロックを更新することです。ディレクトリinodeのデータブロックも更新は、通常ファイルが更新できるかどうかとまったく同じ次元の事です。

最終更新 2014/03/11 02:29:29 - north
(2014/03/10 17:00:19 作成)