Linux内核文件系统10

2021SC@SDUSC

inode.c(1)

今天来分析inode.c文件。有了前面对ext4_jbd2.h、acl.h等头文件的分析做基础,今天的分析将相对简单。

在看代码之前,首先要说一下inode数据结构。inode是Linux内核文件系统中最重要的数据结构之一,里面保存了文件的大小、文件块的大小、创建时间等参数,可以说,一个inode就代表了一个文件。因为软连接、硬连接的存在,指向一个文件的路径可能有多个,即一个文件可以有多个dentry,但是一个文件只能有一个inode。

ext4_inode 定义于/fs/ext4/ext4.h,大小为256字节,也就是说一个4KB的块可以保存16个inode。

/*
 * Structure of an inode on the disk
 */
struct ext4_inode {
	__le16	i_mode;		/*文件模式 */
	__le16	i_uid;		/* 低16位的owner Uid */
	__le32	i_size_lo;	/* 文件大小的字节数 */
	__le32	i_atime;	/* 存取时间 */
	__le32	i_ctime;	/* inode改变时间 */
	__le32	i_mtime;	/* 修改时间 */
	__le32	i_dtime;	/* 删除时间 */
	__le16	i_gid;		/* 低16位 group id */
	__le16	i_links_count;	/* 链接数 */
	__le32	i_blocks_lo;	/* 块数目 */
	__le32	i_flags;	/* 文件标志 */
	union {
		struct {
			__le32  l_i_version;
		} linux1;
		struct {
			__u32  h_i_translator;
		} hurd1;
		struct {
			__u32  m_i_reserved1;
		} masix1;
	} osd1;				/* OS dependent 1 */
	__le32	i_block[EXT4_N_BLOCKS];/* 块指针 */
	__le32	i_generation;	/* 文件版本(适用于NFS) */
	__le32	i_file_acl_lo;	/* 文件的ACL */
	__le32	i_size_high;
	__le32	i_obso_faddr;	/* 弃用片段的地址 */
	union {
		struct {
			__le16	l_i_blocks_high; /* were l_i_reserved1 */
			__le16	l_i_file_acl_high;
			__le16	l_i_uid_high;	/* these 2 fields  */
			__le16	l_i_gid_high;	/* were reserved2[0]  */
			__le16	l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
			__le16	l_i_reserved;
		} linux2;
		struct {
			__le16	h_i_reserved1;	/* 在ext4中被删除的残片号/大小 */
			__u16	h_i_mode_high;
			__u16	h_i_uid_high;
			__u16	h_i_gid_high;
			__u32	h_i_author;
		} hurd2;
		struct {
			__le16	h_i_reserved1;	/* 在ext4中被删除的残片号/大小 */
			__le16	m_i_file_acl_high;
			__u32	m_i_reserved2[2];
		} masix2;
	} osd2;				/* OS dependent 2 */
	__le16	i_extra_isize;
	__le16	i_checksum_hi;	/* crc32c(uuid+inum+inode) BE */
	__le32  i_ctime_extra;  /* 额外的变化(change)时间   (nsec << 2 | epoch) */
	__le32  i_mtime_extra;  /* 额外的修改(Modification)时间   (nsec << 2 | epoch) */
	__le32  i_atime_extra;  /* 额外的访问(Access)时间   (nsec << 2 | epoch) */
	__le32  i_crtime;       /* 文件创建时间 */
	__le32  i_crtime_extra; /* 额外的文件创建时间 (nsec << 2 | epoch) */
	__le32  i_version_hi;	/* 高32位(64位版本) */
	__le32	i_projid;	    /* Project ID */
};

由此可见字段i_block的大小为60个字节,即__le32 i_block[EXT4_N_BLOCKS]且EXT4_N_BLOCKS=15。其中前12个字节为extent头,保存的是extent的基本信息;后48个字节可以保存4个extent节点,每个extent节点为12字节大小。

以下是inode.c文件的代码分析:

/*
*测试一个索引节点是否为快速符号链接。
*快速符号链接的符号链接数据存储在ext4_inode_info->i_data中。
 */
int ext4_inode_is_fast_symlink(struct inode *inode)
{
	if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
		int ea_blocks = EXT4_I(inode)->i_file_acl ?
				EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0;

		if (ext4_has_inline_data(inode))
			return 0;

		return (S_ISLNK(inode->i_mode) && inode->i_blocks - ea_blocks == 0);
	}
	return S_ISLNK(inode->i_mode) && inode->i_size &&
	       (inode->i_size < EXT4_N_BLOCKS * 4);
}

/*
 * 在i_nlink为零的最后一个iput()函数中调用。
 */
void ext4_evict_inode(struct inode *inode)
{
	handle_t *handle;
	int err;
	/*
	 *最终的inode清理和释放:
	 *sb + inode (ext4_orphan_del()),块位图(block bitmap),组描述符(xattr块释放),
	 位图(bitmap),组描述符(inode释放)
	 */
	int extra_credits = 6;
	struct ext4_xattr_inode_array *ea_inode_array = NULL;
	bool freeze_protected = false;

	trace_ext4_evict_inode(inode);

	if (inode->i_nlink) {
		/*
		 *当记录数据脏缓冲区只在日志中跟踪。
		 因此,尽管mm认为一切就绪,可以获取inode,
		 但在运行的事务中仍然可能有一些页面需要写入,或者等待被检查点。
		 因此,调用jbd2_journal_invalidatepage()(通过truncate_inode_pages())来丢弃这些缓冲区可能会导致数据丢失。
		 而且,即使我们没有丢弃这些缓冲区,在获取inode之后,我们也无法找到它们,
		 因此,如果用户试图在事务被检查点之前读取这些缓冲区,就可能看到过期的数据。
		 所以要小心,把所有东西都放到圆盘上。
		 我们使用ei->i_datasync_tid来存储包含inode数据的最新事务。
		 *
		 *注意目录没有这个问题,因为它们不使用页面缓存。
		 */
		if (inode->i_ino != EXT4_JOURNAL_INO &&
		    ext4_should_journal_data(inode) &&
		    (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
		    inode->i_data.nrpages) {
			journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
			tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;

			jbd2_complete_transaction(journal, commit_tid);
			filemap_write_and_wait(&inode->i_data);
		}
		truncate_inode_pages_final(&inode->i_data);

		goto no_delete;
	}

	if (is_bad_inode(inode))
		goto no_delete;
	dquot_initialize(inode);

	if (ext4_should_order_data(inode))
		ext4_begin_ordered_truncate(inode, 0);
	truncate_inode_pages_final(&inode->i_data);

	/*
	 对于带有日志数据的inode,事务提交可能已经污染了inode。
	 Flush worker因为I_FREEING标志而忽略了它,
	 但是我们仍然需要从writeback列表中移除这个inode。
	 */
	if (!list_empty_careful(&inode->i_io_list)) {
		WARN_ON_ONCE(!ext4_should_journal_data(inode));
		inode_io_list_del(inode);
	}

	/*
	 保护我们不被冻结- iput()调用者不需要有任何保护。
	 但是,当我们处于运行的事务中时,我们已经得到了防止冻结的保护,
	 而且由于锁排序约束,我们无法获取进一步的保护。
	 */
	if (!ext4_journal_current_handle()) {
		sb_start_intwrite(inode->i_sb);
		freeze_protected = true;
	}

	if (!IS_NOQUOTA(inode))
		extra_credits += EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb);

	/*
	 ext4_blocks_for_truncate()和extra_credits中都包含块位图、组描述符和inode,所以减去3。
	 */
	handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE,
			 ext4_blocks_for_truncate(inode) + extra_credits - 3);
	if (IS_ERR(handle)) {
		ext4_std_error(inode->i_sb, PTR_ERR(handle));
		/*
		如果我们要跳过正常的清理,我们仍然需要确保核内孤立链表被正确清理。 
		 */
		ext4_orphan_del(NULL, inode);
		if (freeze_protected)
			sb_end_intwrite(inode->i_sb);
		goto no_delete;
	}

	if (IS_SYNC(inode))
		ext4_handle_sync(handle);

	/*
	 在调用 ext4_truncate() 之前将 inode->i_size 设置为 0。 
	 这里我们需要对符号链接进行特殊处理,
	 因为 i_size 用于确定 ext4_inode_info->i_data 是否包含符号链接数据或块映射。 
	 将 i_size 设置为 0 将删除其快速符号链接状态。 
	 擦除 i_data 使其成为有效的空块映射。
	 */
	if (ext4_inode_is_fast_symlink(inode))
		memset(EXT4_I(inode)->i_data, 0, sizeof(EXT4_I(inode)->i_data));
	inode->i_size = 0;
	err = ext4_mark_inode_dirty(handle, inode);
	if (err) {
		ext4_warning(inode->i_sb,
			     "couldn't mark inode dirty (err %d)", err);
		goto stop_handle;
	}
	if (inode->i_blocks) {
		err = ext4_truncate(inode);
		if (err) {
			ext4_error_
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值