揭开虚拟文件系统的云雾(1)(基于linux1.2.13)

这一篇我们来看看,虚拟文件系统是如何抹平各个文件系统的差异,又是如何和具体的文件系统串起来的。
我们先来回顾一下之前的讲的内容。

	void mount_root(void){
		...
		
	for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
		if(retval)
			break;
		// 没有关联到设备则不需要往下执行,有些文件系统是没有对应的底层设备的
		if (!fs_type->requires_dev)
			continue;
		// 读根设备的超级块,设备的第一扇区是分区表,接着是超级块
		sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
		// 读取成功
		if (sb) {
			// 根节点 
			inode = sb->s_mounted;
			inode->i_count += 3 ;	/* NOTE! it is logically used 4 times, not 1 */
			sb->s_covered = inode;
			sb->s_flags = root_mountflags;
			// 当前进程(init进程)的根目录和工作目录设置为根节点
			current->fs->pwd = inode;
			current->fs->root = inode;
			printk ("VFS: Mounted root (%s filesystem)%s.\n",
				fs_type->name,
				(sb->s_flags & MS_RDONLY) ? " readonly" : "");
			// 直接返回,即第一个读取成功的文件系统成为根文件系统
			return;
		}
	}
		...
	}

我们看到由read_super加载超级块的内容,假设根文件系统是ext文件系统。从函数sys_setup中我们知道ext文件系统的read_super函数是ext_read_super。

#ifdef CONFIG_EXT_FS
	register_filesystem(&(struct file_system_type)
		{ext_read_super, "ext", 1, NULL});
#endif

接着往下看。

struct super_block *ext_read_super(struct super_block *s,void *data, 
				   int silent)
{
	struct buffer_head *bh;
	struct ext_super_block *es;
	int dev = s->s_dev,block;

	lock_super(s);
	set_blocksize(dev, BLOCK_SIZE);
	// 读取设备的内容,即超级块的内容
	if (!(bh = bread(dev, 1, BLOCK_SIZE))) {
		s->s_dev=0;
		unlock_super(s);
		printk("EXT-fs: unable to read superblock\n");
		return NULL;
	}
	// 文件系统的一些属性
	es = (struct ext_super_block *) bh->b_data;
	// 超级块的一些属性
	...
	s->s_dev = dev;
	// 操作函数集
	s->s_op = &ext_sops;
	// 读取根节点,超级块的s_mounted字段指向根节点
	if (!(s->s_mounted = iget(s,EXT_ROOT_INO))) {
		s->s_dev=0;
		printk("EXT-fs: get root inode failed\n");
		return NULL;
	}
	return s;
}

我们看到这个函数主要做的几件事情是
1 从硬盘读取超级块的内容
2 设置超级块的操作函数集
3 最后读取文件系统的根节点。
读超级块内容这个没有太多需要解释的,我们先看看超级块操作函数集的定义

static struct super_operations ext_sops = { 
	ext_read_inode,
	NULL,
	ext_write_inode,
	ext_put_inode,
	ext_put_super,
	ext_write_super,
	ext_statfs,
	NULL
};

然后我们看一下读取inode的函数iget。

extern inline struct inode * iget(struct super_block * sb,int nr)
{
	return __iget(sb,nr,1);
}

__iget函数的主要逻辑是获取一个新的inode结构体,然后执行read_inode(inode)从硬盘把inode数据读取进来(inode.c后续单独分析)。核心代码如下。

if (!empty) {
		h->updating++;
		// 获取一个空闲inode
		empty = get_empty_inode();
		if (!--h->updating)
			wake_up(&update_wait);
		if (empty)
			goto repeat;
		return (NULL);
	}
	inode = empty;
	inode->i_sb = sb;
	inode->i_dev = sb->s_dev;
	inode->i_ino = nr;
	inode->i_flags = sb->s_flags;
	put_last_free(inode);
	insert_inode_hash(inode);
	read_inode(inode);

下面直接看read_inode函数。

static void read_inode(struct inode * inode)
{
	lock_inode(inode);
	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode)
		inode->i_sb->s_op->read_inode(inode);
	unlock_inode(inode);
}

我们看文章的开头可以发现,read_inode函数在ext文件系统中的实现函数是ext_read_inode。

void ext_read_inode(struct inode * inode)
{
	struct buffer_head * bh;
	struct ext_inode * raw_inode;
	int block;
	// 每个硬盘块可以存储的inode结构体数,+2是代表启动扇区、超级块数据的硬盘块
	block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK;
	// 读取某设备的某个块
	if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE)))
		panic("unable to read i-node block");
	// 取余算出偏移从而得到inode结构体的数据
	raw_inode = ((struct ext_inode *) bh->b_data) +
		(inode->i_ino-1)%EXT_INODES_PER_BLOCK;
	// 复制某些字段到内存的inode结构体
	inode->i_mode = raw_inode->i_mode;
	inode->i_uid = raw_inode->i_uid;
	inode->i_gid = raw_inode->i_gid;
	inode->i_nlink = raw_inode->i_nlinks;
	inode->i_size = raw_inode->i_size;
	inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
	inode->i_blocks = inode->i_blksize = 0;
	// 字符和块设备的i_zone[0]是设备号
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
		inode->i_rdev = raw_inode->i_zone[0];
	else for (block = 0; block < 12; block++)
		// 否则i_zone里存的是文件数据的硬盘直接块号或者间接块号 
		inode->u.ext_i.i_data[block] = raw_inode->i_zone[block];
	brelse(bh);
	inode->i_op = NULL;
	// 不同类型的文件赋值不同的操作函数集
	if (S_ISREG(inode->i_mode))
		inode->i_op = &ext_file_inode_operations;
	else if (S_ISDIR(inode->i_mode))
		inode->i_op = &ext_dir_inode_operations;
	else if (S_ISLNK(inode->i_mode))
		inode->i_op = &ext_symlink_inode_operations;
	else if (S_ISCHR(inode->i_mode))
		inode->i_op = &chrdev_inode_operations;
	else if (S_ISBLK(inode->i_mode))
		inode->i_op = &blkdev_inode_operations;
	else if (S_ISFIFO(inode->i_mode))
		init_fifo(inode);
}

至此,把根文件系统的超级块和根节点inode都读取进来了。再回头看mount_root函数的代码

	inode = sb->s_mounted;
	// 当前进程(init进程)的根目录和工作目录设置为根节点
	current->fs->pwd = inode;
	current->fs->root = inode;

下面我们看看此时的架构图。

这一篇就先说到这里,现在有根节点了,下一篇看一下如何进行文件的读写和多文件系统是如何一起工作的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值