Q:ext2的磁盘inode结构体为什么不存放文件inode号?
A:用户态程序查找文件都是通过文件路径来完成的,文件的inode号都是存储在父目录的数据块中,在查找过程中,根据文件名,可以很快的从目录的数据块中找到ext2_dir_entry_2,继而拿到inode号,拿到inode号后,从inode表就可以很快的找到磁盘inode结构.换言之,磁盘inode即使存储了inode号,除了用于校验,没什么其他用处.
Q:新创建的VFS inode为什么要置位I_LOCK?
A:VFS inode创建的过程:
ext2_iget()
iget_locked()
//由超级块和inode号生成哈希值
struct hlist_head *head = inode_hashtable + hash(sb, ino);
if((inode = ifind_fast()) != NULL){
inode = find_inode_fast(sb, head, ino);
__iget(inode);
//等待I_LOCK标志清除,表示inode的成员赋值完毕
wait_on_inode(inode);
return inode;//从inode缓存中找到inode
}
else//inode不在icache中,需要读取磁盘inode并创建VFS inode
get_new_inode_fast()
inode = alloc_inode(sb);
inode->i_ino = ino;
hlist_add_head(&inode->i_hash, head);
raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);//读取磁盘inode
//将磁盘inode的信息赋值给VFS inode
进程1创建好VFS inode后会立即将inode加入到icache中,但此时inode的成员还未赋值,inode还不能被其他进程直接使用,通过置I_LOCK标志,inode的成员赋值完毕后,清除I_LOCK标志的方式,当其他进程从icache中如果找到I_LOCK置位的inode时需等待,直到inode的成员赋值完毕后,等待的进程被唤醒,安全的使用赋值完毕的inode.
Q:多个进程调用sys_open()打开同一个文件后是否会增加VFS inode的引用计数?
A:不会,系统调用路径中看到的VFS inode是通过dentry看到的,inode的用户是dentry而不是系统调用路径,dentry的用户才是系统调用路径,因此,多个进程打开相同的文件时,只会增加dentry的引用计数,不会增加inode的引用计数.
函数调用过程:
do_sys_open()
fd = get_unused_fd_flags(flags);
struct file *f = do_filp_open(dfd, tmp, flags, mode);
path_lookup_open()
do_path_lookup()
path_walk()
link_path_walk()
do_lookup()=>
fd_install()//将fd和file关联,fd返回给用户态程序
do_lookup()
if(dentry = __d_lookup() == NULL){
dentry = real_lookup() //dcache中找不到,到文件系统找
dentry = d_alloc(parent, name);//无论是否能找到,先分配dentry
atomic_set(&dentry->d_count, 1);//引用计数为1
dir->i_op->lookup()
ext2_lookup()
//由文件名从目录的数据块中找到dentry的inode号
ino = ext2_inode_by_name(dir, dentry);
//根据inode号找到并创建相应的inode
inode = ext2_iget(dir->i_sb, ino)=>
d_splice_alias()
d_add(dentry, inode);
//将dentry和inode关联,此后dentry不再是负状态
d_instantiate(entry, inode);
if (inode)
list_add(&entry->d_alias, &inode->i_dentry);
entry->d_inode = inode;
//将dentry加入到dcache
d_rehash(entry);
_d_rehash(entry);
entry->d_flags &= ~DCACHE_UNHASHED;
list = d_hash(entry->d_parent, entry->d_name.hash)
hlist_add_head_rcu(&entry->d_hash, list);
}
path->mnt = mnt;
path->dentry = dentry;
__follow_mount(path);
while (d_mountpoint(path->dentry)) {
struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry);
//进入到新文件系统,如果新文件系统的根目录还挂载了文件系统,继续循环
//直到进入最新挂载的文件系统.很自然,新文件系统会将挂载点下所有
//的文件和目录都隐藏
path->mnt = mounted;
path->dentry = dget(mounted->mnt_root);
}
ext2_iget()
inode *inode = iget_locked(sb, ino); //vfs inode
//由超级块和inode号生成哈希值
struct hlist_head *head = inode_hashtable hash(sb, ino);
if((inode = ifind_fast()) != NULL){
return inode;//从inode缓存中找到inode
}
get_new_inode_fast()
inode = alloc_inode(sb);
ext2_alloc_inode()
struct address_space * mapping = &inode->i_data;
mapping->assoc_mapping = NULL;
inode->i_mapping = &inode->i_data;
inode->i_ino = ino;
list_add(&inode->i_list, &inode_in_use);
list_add(&inode->i_sb_list, &sb->s_inodes);
hlist_add_head(&inode->i_hash, head);
inode->i_state = I_LOCK|I_NEW;
ext2_inode_info *ei = EXT2_I(inode); //内存inode
ext2_inode *raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);//根据inode号读取磁盘inode
//每个块组中inode数目是固定的,根据inode号可以计算出inode在表中的偏移
offset = ((ino - 1) % EXT2_INODES_PER_GROUP(sb)) * EXT2_INODE_SIZE(sb);
block = le32_to_cpu(gdp->bg_inode_table)
(offset >> EXT2_BLOCK_SIZE_BITS(sb));//计算inode所在的磁盘逻辑块号
if (!(bh = sb_bread(sb, block)))
goto Eio;
*p = bh;
offset &= (EXT2_BLOCK_SIZE(sb) - 1);//找到在块内的偏移
return (struct ext2_inode *) (bh->b_data offset);
//由磁盘inode初始化vfs inode
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
...
//由磁盘inode初始化内存inode
ext2_inode_info->i_flags = le32_to_cpu(raw_inode->i_flags);
//根据inode号计算出inode所在的块组
ext2_inode_info->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
...
inode->i_mapping->a_ops = &ext2_aops;
return inode;