inode的i_nlink(linux4.0.4)

一、背景

1)通过命令ln a b创建硬链接b->a后(a是一个普通文件),stat a命令看到Links值是多少?

gsf@ubuntu:~$ stat a

  File: 'a'

  Size: 0             Blocks: 0          IO Block: 4096   regular empty file

Device: 801h/2049d  Inode: 1442959     Links: 2

2)一个空目录dirA,stat命令看到的Links值是多少?

gsf@ubuntu:~$ stat dirA

  File: 'dirA'

  Size: 4096         Blocks: 8          IO Block: 4096   directory

Device: 801h/2049d  Inode: 1442960     Links: 2

      在dirA中创建一个文件后,dirA的Links值是多少?

gsf@ubuntu:~$ stat dirA

  File: 'dirA'

  Size: 4096         Blocks: 8          IO Block: 4096   directory

Device: 801h/2049d  Inode: 1442960     Links: 2

    在dirA中创建一个目录后,dirA的Links值是多少?

gsf@ubuntu:~$ stat dirA

  File: 'dirA'

  Size: 4096         Blocks: 8          IO Block: 4096   directory

Device: 801h/2049d  Inode: 1442960     Links: 3

以上问题涉及到文件的“链接计数”概念,即inode的i_nlink字段。

创建一个普通文件a,它的链接计数”是1,创建该文件的硬链接b,a与b的数据块在磁盘上是相同的(或者说inode是相同的),因两个文件引用同一个数据块,所以创建硬链接时将“链接计数”加1。

创建一个目录dirA,dirA本身一个硬链接计数,还有一个表示当前目录的“.”代表dirA,所以目录dirA的硬链接计数为2。在dirA中创建一个子目录,子目录的”..”代表dirA,所以dirA的硬链接计数变为3。

 

二、理论知识

linux中文件是以inode来标识的,不同的inode代表不同的文件。磁盘上存储着inode的信息,以ext3为例,磁盘上的inode结构为struct ext3_inode。为避免每次直接读写磁盘inode信息(性能低),内核会把磁盘inode读入内存,并填充一些额外信息构成vfs的inode,vfs inode定义为struct inode。内核写内存inode后将其标记为dirty状态,在适当的时候写入磁盘。inode有一个“链接计数“概念,这就是本文要讨论的主题。

struct ext3_inode-->i_links_count、struct inode-->i_nlink字段分别是磁盘inode、内存inode中的“链接计数”字段。unlink删除文件时先将磁盘上对应的ext3_dir_entry_2“无效”掉(这个“无效”操作并不是指直接把磁盘上的ext3_dir_entry_2删除,而是把前一个ext3_dir_entry_2的rec_len字段适当地增加长度就可以了,这与ext3的磁盘layout有关,不属于本文要描述的问题),然后将该链接计数”减1,如果此时“链接计数”为0,则说明该文件真的没有人引用了,可以释放磁盘inode了。当文件的ext3_dir_entry_2被“无效”掉后,文件系统已经无法通过该ext3_dir_entry_2找到这个文件了,但是依然可以通过其他ext3_dir_entry_2找到该文件。具体来说硬链接b-->a(硬链接文件的inode是一样的),删除a后,文件系统把a对应的ext3_dir_entry_2“无效”掉了,也就是不存在a这个文件了,但是依然可以通过b对应的ext3_dir_entry_2找到文件。

三、代码分析

1)文件的删除

SYSCALL_DEFINE1(unlink, const char __user *, pathname)--> do_unlinkat-->vfs_unlink:

int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)

{

      struct inode *target = dentry->d_inode;

 

      mutex_lock(&target->i_mutex);

      if (is_local_mountpoint(dentry))

           error = -EBUSY;

      else {

           error = security_inode_unlink(dir, dentry);

           if (!error) {

                 error = try_break_deleg(target, delegated_inode);

                 if (error)

                      goto out;

                 error = dir->i_op->unlink(dir, dentry);

           }

      }

其中的dir->i_op->unlink即ext3_unlink,关键代码如下:

static int ext3_unlink(struct inode * dir, struct dentry *dentry)

{

      int retval;

      struct inode * inode;

      struct buffer_head * bh;

      struct ext3_dir_entry_2 * de;

 

      bh = ext3_find_entry(dir, &dentry->d_name, &de);

      inode = dentry->d_inode;

      retval = ext3_delete_entry(handle, dir, de, bh);

      ext3_mark_inode_dirty(handle, dir);

      drop_nlink(inode);

      if (!inode->i_nlink)

           ext3_orphan_add(handle, inode);

 

ext3_find_entry从磁盘上找到待删除文件的ext3_dir_entry_2结构,然后通过ext3_delete_entry将找到的ext3_dir_entry_2结构从磁盘layout上“无效”掉,然后将待删除文件的目录的inode标记为dirty状态,在适当的时候会同步到磁盘上,接着通过drop_nlink将待删除文件的“链接计数”减1。ext3_orphan_add也是删除文件中一个重要的操作,避免系统突然断电导致inode对应的数据块无法回收的问题,这在其他文章中描述。

2)目录的删除

在linux中,任何东西都是文件,目录也是文件,但删除目录与删除文件却是有一点区别的,正如第一部分中第2个问题描述的,任何目录都有“.”及“..”影响着“链接计数”,代码如下:

SYSCALL_DEFINE1(rmdir, const char __user *, pathname)--> do_rmdir--> vfs_rmdir:

int vfs_rmdir(struct inode *dir, struct dentry *dentry)

{

      error = dir->i_op->rmdir(dir, dentry);

dir->i_op->rmdir即是ext3_rmdir,关键代码如下:

static int ext3_rmdir (struct inode * dir, struct dentry *dentry)

{

      int retval;

      struct inode * inode;

      struct buffer_head * bh;

      struct ext3_dir_entry_2 * de;

 

      bh = ext3_find_entry(dir, &dentry->d_name, &de);

 

      inode = dentry->d_inode;

 

 

      retval = ext3_delete_entry(handle, dir, de, bh);

      if (inode->i_nlink != 2)

           ext3_warning (inode->i_sb, "ext3_rmdir",

                       "empty directory has nlink!=2 (%d)",

                       inode->i_nlink);

 

      clear_nlink(inode);

 

      ext3_orphan_add(handle, inode);

      ext3_mark_inode_dirty(handle, inode);

      drop_nlink(dir);

大致操作与删除文件一样。ext3_find_entry从磁盘上找到待删除目录的ext3_dir_entry_2结构,然后通过ext3_delete_entry将找到的ext3_dir_entry_2结构从磁盘layout上“无效”掉,接着通过clear_nlink将待删除目录的“链接计数”设为0(因为目录不可能是硬链接,也不可能删除一个非空目录,所以直接设为0)。ext3_orphan_add作用同删除文件中描述的一样。最后的drop_nlink是将父目录的硬链接计数减1,因为被删掉的目录的”..”是父目录硬链接。

四、i_nlink的演变

在vfs inode的定义中,有i_nlink及__i_nlink字段,两者其实是一个东西,这样做的目录是:

Prevent direct modification of i_nlink by making it const and adding a non-const  __i_nlink alias.(引用自 http://lkml.iu.edu/hypermail/linux/kernel/1110.1/01493.html

struct inode {

unsigned long       i_ino;

      /*

       * Filesystems may only read i_nlink directly.  They shall use the

       * following functions for modification:

       *

       *    (set|clear|inc|drop)_nlink

       *    inode_(inc|dec)_link_count

       */

      union {

           const unsigned int i_nlink;

           unsigned int __i_nlink;

      };

}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值