1.添加inode到inode cache链表
当inode的引用计数器i_count为0后,会调用iput_final去释放
c
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。
c
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;
}
c
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
c
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缓存)