inode生命周期

1.添加inode到inode cache链表

inode生命周期_第1张图片

当inode的引用计数器i_count为0后,会调用iput_final去释放

static void iput_final(struct inode *inode)
{
	struct super_block *sb = inode->i_sb;
	const struct super_operations *op = inode->i_sb->s_op;
	unsigned long state;
	int drop;

	WARN_ON(inode->i_state & I_NEW);

	if (op->drop_inode)
		drop = op->drop_inode(inode);
	else
		drop = generic_drop_inode(inode);
	
	//引用计数为0之后,将inode放到lru中,等待回收
	if (!drop &&
	    !(inode->i_state & I_DONTCACHE) &&
	    (sb->s_flags & SB_ACTIVE)) {
		inode_add_lru(inode);
		spin_unlock(&inode->i_lock);
		return;
	}
	
	//对于已经被文件系统除名的文件,即i_nlink = 0, 不必再保留inode,直接释放掉
	WRITE_ONCE(inode->i_state, state | I_FREEING);
	if (!list_empty(&inode->i_lru))
		inode_lru_list_del(inode);
	spin_unlock(&inode->i_lock);

	evict(inode);
}

2.从inode cache中删除inode

在系统需要回收内存时,就会对这个链表下手,回收最近最少使用的inode。

long prune_icache_sb(struct super_block *sb, struct shrink_control *sc)
{
	LIST_HEAD(freeable);
	long freed;
	//遍历超级块的s_inode_lru链表,按照回收控制结构sc指定的回收数量,
	//将可回收的inode隔离到freeable链表中集中回收
	freed = list_lru_shrink_walk(&sb->s_inode_lru, sc,
				     inode_lru_isolate, &freeable);
	//将隔离出来的inode进行回收,这样隔离后可以避免锁竞争
	dispose_list(&freeable);
	return freed;
}

static void dispose_list(struct list_head *head)
{
	while (!list_empty(head)) {
		struct inode *inode;
		
		inode = list_first_entry(head, struct inode, i_lru);
		//将inode从超级块的s_inode_lru链表摘除
		list_del_init(&inode->i_lru);
		//回收inode
		evict(inode);
		cond_resched();
	}
}

static void evict(struct inode *inode)
{
	const struct super_operations *op = inode->i_sb->s_op;

	BUG_ON(!(inode->i_state & I_FREEING));
	BUG_ON(!list_empty(&inode->i_lru));
	//从bdi_writeback的b_io链表摘除
	if (!list_empty(&inode->i_io_list))
		inode_io_list_del(inode);
	//将inode从超级块的s_inodes链表摘除
	inode_sb_list_del(inode);

	//等待该inode回写完毕
	inode_wait_for_writeback(inode);
	//调用对应文件系统的evict_inode方法,回写pagecache
	if (op->evict_inode) {
		op->evict_inode(inode);
	} else {
		truncate_inode_pages_final(&inode->i_data);
		clear_inode(inode);
	}
	//如果是块设备inode
	if (S_ISBLK(inode->i_mode) && inode->i_bdev)
		bd_forget(inode);
	//如果是字符型设备
	if (S_ISCHR(inode->i_mode) && inode->i_cdev)
		cd_forget(inode);
	//从全局inode哈希表中摘除
	remove_inode_hash(inode);
	...
	//回收inode
	destroy_inode(inode);
}

处理完这些引用后,就可以调用destroy_inode回收到slab缓存,对于ext4,调用的是ext4_destroy_inode

static void destroy_inode(struct inode *inode)
{
	BUG_ON(!list_empty(&inode->i_lru));
	__destroy_inode(inode);
	//调用对应文件系统的destroy_inode方法,将inode回收到slab缓存
	//对于ext4,调用的是ext4_destroy_inode
	if (inode->i_sb->s_op->destroy_inode)
		inode->i_sb->s_op->destroy_inode(inode);
	else
		call_rcu(&inode->i_rcu, i_callback);
}

static void ext4_destroy_inode(struct inode *inode)
{	
	if (!list_empty(&(EXT4_I(inode)->i_orphan))) {
		...
	}
	//调用ext4_i_callback将inode释放会slab缓存
	call_rcu(&inode->i_rcu, ext4_i_callback);
}

static void ext4_i_callback(struct rcu_head *head)
{
	struct inode *inode = container_of(head, struct inode, i_rcu);
	//释放回slab缓存
	kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
}

3.记忆

  • 等待回收:iput_final会把i_count为0的inode放入superblock的LRU链表s_inode_lru【这个链表也称为inode cache】中(inode_lru_list_add函数)
  • 正式回收:回收链表s_inode_lru中的节点,大致调用栈:evict->destroy_inode -> ext4_destroy_inode -> ext4_i_callback(释放slab缓存)

你可能感兴趣的:(linux)