当前位置:操作系统 > Unix/Linux >>

Linuxinodecache机制分析

2002年11月8日 16:24

  作 者: 詹荣开

  (詹荣开 zhanrk@sohu.com)

  Linux inode cache机制实现在fs/inode.c文件中。

  1.1.Inode的slab分配器缓存

  索引节点缓存(inode cache,简称icache)机制的实现是以inode对象的slab分配器缓存为基础的,因此要从物理内存中申请或释放一个inode对象,都必须通过kmem_cache_alloc()函数和kmem_cache_free()函数来进行。

  Inode对象的slab分配缓存由一个kmem_cache_t类型的指针变量inode_cachep来定义。这个slab分配器缓存是在inode cache的初始化函数inode_init()中通过kmem_cache_create()函数来创建的。

  Linux在inode.c文件中又定义了两个封装函数,来实现从inode_cachep slab分配器缓存中分配一个inode对象或将一个不再使用的inode对象释放给slab分配器,如下所示:

  #define alloc_inode() \\

  ((struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL))

  static void destroy_inode(struct inode *inode)

  {

  if (!list_empty(&inode->i_dirty_buffers))

  BUG();

  kmem_cache_free(inode_cachep, (inode));

  }

  1.2 和inode对象相关的一些底层操作

  源文件inode.c中实现了一些对inode对象的底层基本操作,如下:

  (1)clean_inode()——初始化部分inode对象成员域

  该函数用来将一个刚从inode_cachep slab分配器中分配得到的inode对象中的某些成员初始化为已知的值(通常为0),但是有一个例外,既链接数i_nlink被初始化为1。这是一个静态的静态内部函数,因此它只能被inode.c中的其他函数所调用,如:get_empty_inode()和get_new_inode()。

  /*

  * This just initializes the inode fields

  * to known values before returning the inode..

  *

  * i_sb, i_ino, i_count, i_state and the lists have

  * been initialized elsewhere..

  */

  static void clean_inode(struct inode *inode)

  {

  static struct address_space_operations empty_aops;

  static struct inode_operations empty_iops;

  static struct file_operations empty_fops;

  memset(&inode->u, 0, sizeof(inode->u));

  inode->i_sock = 0;

  inode->i_op = &empty_iops;

  inode->i_fop = &empty_fops;

  inode->i_nlink = 1; /* NOTE!i_nlink被初始化为1 */

  atomic_set(&inode->i_writecount, 0);

  inode->i_size = 0;

  inode->i_generation = 0;

  memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));

  inode->i_pipe = NULL;

  inode->i_bdev = NULL;

  inode->i_data.a_ops = &empty_aops;

  inode->i_data.host = inode;

  inode->i_mapping = &inode->i_data;

  }

  (2)get_empty_inode()——从slab分配器中分配一个空的inode对象

  该函数通过调用alloc_inode宏从slab分配器中分配一个inode对象,然后把除了I_count引用计数和链接计数I_nlink之外的所有域都初始化为NULL(部分域的初始化通过调用clean_inode函数来完成),并将这个inode对象链入inode_in_use链表中。最后返回这个inode对象的指针,如下所示:

  struct inode * get_empty_inode(void)

  {

  static unsigned long last_ino;

  struct inode * inode;

  inode = alloc_inode();

  if (inode)

  {

  spin_lock(&inode_lock);

  inodes_stat.nr_inodes++;

  list_add(&inode->i_list, &inode_in_use);

  inode->i_sb = NULL;

  inode->i_dev = 0;

  inode->i_ino = ++last_ino;

  inode->i_flags = 0;

  atomic_set(&inode->i_count, 1);

  inode->i_state = 0;

  spin_unlock(&inode_lock);

  clean_inode(inode);

  }

  return inode;

  }

  Linux内核模块通常并不会调用这个函数来分配一个inode对象。那些想获取一个没有索引节点号的inode对象的内核模块(如网络层等),以及那些没有任何已知信息的fs,通常会用这个函数来获取一个新的inode对象。

  (3) clear_inode()——清除一个inode对象中的内容

  在调用destroy_inode()函数释放一个inode对象之前,通常调用该函数来清除该inode对象中内容,如:使inode引用的缓冲区无效、解除对其它对象的引用等。

  /**

  * clear_inode - clear an inode

  * @inode: inode to clear

  *

  * This is called by the filesystem to tell us

  * that the inode is no longer useful. We just

  * terminate it with extreme prejudice.

  */

  void clear_inode(struct inode *inode)

  {

  if (!list_empty(&inode->i_dirty_buffers))

  invalidate_inode_buffers(inode);

  if (inode->i_data.nrpages)

  BUG();

  if (!(inode->i_state & I_FREEING))

  BUG();

  if (inode->i_state & I_CLEAR)

  BUG();

  wait_on_inode(inode);

  if (IS_QUOTAINIT(inode))

  DQUOT_DROP(inode);

  if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)

  inode->i_sb->s_op->clear_inode(inode);

  if (inode->i_bdev) {

  bdput(inode->i_bdev);

  inode->i_bdev = NULL;

  }

  inode->i_state = I_CLEAR;

  }

  1.3 icache数据结构

  Linux通过在inode_cachep slab分配器缓存之上定义各种双向链表来实现inode缓存机制,以便有效地管理内存inode对象。这些链表包括:正在使用的inode链表、未使用的inode链表、inode哈希链表和匿名inode的哈希链表,他们的定义如下:

  static LIST_HEAD(inode_in_use);

  static LIST_HEAD(inode_unused);

  static struct list_head *inode_hashtable;

  static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */

  此外,每个超级块对象super_block中还有一条被修改过的、且正在使用的inode双向链表s_dirty。

  每一个inode对象都会存在于两个分离的双向链表中:

  (1)一个就是inode哈希链表inode_hashtable,用来加快inode查找,每个inode对象都通过I_hash指针链入哈希链表中。

  (2)另一个就是inode的“类型”链表:

  l 如果I_count>0、I_nlink>0且该inode不脏,则这个inode就通过其I_list指针链入系统全局的inode_in_use双向链表。

  l 如果I_count和I_nlink都大于0,但是这个inode为脏(既I_state域中设置了I_DIRTY标志),则这个inode通过I_list指针链入他所属的super_block对象的s_dirty链表。

  l 如果I_count=0,则通过其I_list链入inode_unused链表。

  对于那些不属于任何超级块对象(即I_sb=NULL)的匿名inode对象,则通过I_hash指针链入系统全局的匿名inode哈希链表anon_hash_chain。

  1.3.1 对inode缓存链表的锁保护

  Linux在inode.c中定义了自旋锁inode_lock,来实现对所有inode缓存链表的互斥访问。也即,任何访问任意一条inode缓存链表的代码段,都必须通过调用spin_lock()函数持有该自旋锁,并在结束访问后通过spin_unlock()释放该自旋锁。Inode_lock的定义如下:

  Spinlock_t inode_lock=SPIN_LOCK_UNLOCKED;

  NOTE!如果要改变一个正在使用的inode对象的I_state域,也必须先持有该自旋锁。

  1.3.2 inode缓存的统计信息

  全局变量inodes_stat定义了inode cache的统计信息,主要包括cache中的inode对象总数和其中未使用的inode个数,其定义如下:

  struct {

  int nr_inodes;

  int nr_unused;

  int dummy[5];

  } inodes_stat;

  1.3.3 inode哈希链表

  inode哈希链表的主要用途是加快在icache中查找一个特定的inode对象。指针inode_hashtable指向一组哈希链表表头,所有哈希函数值(记为h)相同的inode对象都通过I_hash指针作为接口组成双向链表,并挂在inode_hashtable[h]这个哈希链表表头之后。所有哈希链表表头都放在一起组成一
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,