上一篇博文我们详细分析了VFS如何将具体文件系统ext2的超级快对象缓存到内存中,主要是利用了ext2_sb_info数据结构。ext2_fill_super函数最后的时候,会将传入该函数的super_block类型的参数sb赋予以下的值:
sb->s_op = &ext2_sops;
其中,ext2_sops是将super_block的super_operations初始化以下方法的集合:
static struct super_operations ext2_sops = {
.alloc_inode = ext2_alloc_inode,
.destroy_inode = ext2_destroy_inode,
.read_inode = ext2_read_inode,
.write_inode = ext2_write_inode,
.put_inode = ext2_put_inode,
.delete_inode = ext2_delete_inode,
.put_super = ext2_put_super,
.write_super = ext2_write_super,
.statfs = ext2_statfs,
.remount_fs = ext2_remount,
.clear_inode = ext2_clear_inode,
.show_options = ext2_show_options,
#ifdef CONFIG_QUOTA
.quota_read = ext2_quota_read,
.quota_write = ext2_quota_write,
#endif
};
本篇博文,我们来讨论VFS如何将ext2的索引节点缓存到内存中。
在打开文件时,要执行路径名查找。对于不在目录项高速缓存内的路径名元素,会创建一个新的目录项对象和索引节点对象(参见“标准路经名查找”博文)。当VFS访问一个Ext2磁盘索引节点时,它会创建一个ext2_inode_info类型的索引节点描述符:
struct ext2_inode_info {
__le32 i_data[15];
__u32 i_flags;
__u32 i_faddr;
__u8 i_frag_no;
__u8 i_frag_size;
__u16 i_state;
__u32 i_file_acl;
__u32 i_dir_acl;
__u32 i_dtime;
/*
* i_block_group is the number of the block group which contains
* this file's inode. Constant across the lifetime of the inode,
* it is ued for making block allocation decisions - we try to
* place a file's data blocks near its inode block, and new inodes
* near to their parent directory's inode.
*/
__u32 i_block_group;
/*
* i_next_alloc_block is the logical (file-relative) number of the
* most-recently-allocated block in this file. Yes, it is misnamed.
* We use this for detecting linearly ascending allocation requests.
*/
__u32 i_next_alloc_block;
/*
* i_next_alloc_goal is the *physical* companion to i_next_alloc_block.
* it the the physical block number of the block which was most-recently
* allocated to this file. This give us the goal (target) for the next
* allocation when we detect linearly ascending requests.
*/
__u32 i_next_alloc_goal;
__u32 i_prealloc_block;
__u32 i_prealloc_count;
__u32 i_dir_start_lookup;
#ifdef CONFIG_EXT2_FS_XATTR
/*
* Extended attributes can be read independently of the main file
* data. Taking i_mutex even when reading would cause contention
* between readers of EAs and writers of regular file data, so
* instead we synchronize on xattr_sem when reading or changing
* EAs.
*/
struct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT2_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
rwlock_t i_meta_lock;
struct inode vfs_inode;
};
该描述符包含下列信息:
- 存放在vfs mode字段的整个VFS索引节点对象;
- ext2磁盘索引节点对象结构中的大部分字段(不保存在VFS索引节点中的那些字段);
- 块组中索引节点对应的索引i_block_group;
- i_next_alloc_block和i_next_alloc_goal分别存放着最近为文件分配的磁盘块的逻辑块号和物理块号;
- i_prealloc_block和i_prealloc_count字段,用于数据块预分配;
- xattr_sem字段,一个读写信号量,允许增强属性与文件数据同时读入;
- i_acl和i_default_acl字段,指向文件的访问控制列表。
当处理Ext2文件时,alloc_inode超级块方法是由ext2_alloc_inode()函数实现:
static struct inode *ext2_alloc_inode(struct super_block *sb)
{
struct ext2_inode_info *ei;
ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL);
if (!ei)
return NULL;
#ifdef CONFIG_EXT2_FS_POSIX_ACL
ei->i_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif
ei->vfs_inode.i_version = 1;
return &ei->vfs_inode;
}
该函数的实现极其简单,它首先从ext2_inode_cachep slab分配器高速缓存得到一个ext2_inode_info数据结构,然后返回在这个ext2_inode_info数据结构中的索引节点对象的地址ei->vfs_inode。
我们看到,跟ext2_sb_info和ext2_group_desc不同,ext2_inode_info根本没有对应的buffer_head字段,也就是说,它只是把ext2_inode的某些字段拷贝进来,对位于磁盘的ext2_inode进行动态缓存,下面,我们就把ext2数据结构的VFS映像做个总结:
类型 | 磁盘数据结构 | 内存数据结构 | 缓存模式 |
Superblock | ext2_super_block | ext2_sb_info | 总是缓存 |
Group descriptor | ext2_group_desc | ext2_group_desc | 总是缓存 |
Block bitmap | Bit array in block | Bit array in buffer | 动态缓存 |
inode bitmap | Bit array in block | Bit array in buffer | 动态缓存 |
inode | ext2_inode | ext2_inode_info | 动态缓存 |
Data block | Array of bytes | VFS buffer | 动态缓存 |
Free inode | ext2_inode | None | 从不缓存 |
Free block | Array of bytes | None | 从不缓存 |