首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >虚拟文件系统源码解析之open(基于linux1.2.13)

虚拟文件系统源码解析之open(基于linux1.2.13)

作者头像
theanarkh
发布2019-12-18 16:18:56
6940
发布2019-12-18 16:18:56
举报
文章被收录于专栏:原创分享原创分享

我们操作一个文件之前都需要先open一下。我们看看open在虚拟文件系统中大致的执行过程。不会分析具体的过程。主要分析一下虚拟文件系统的实现原理。

asmlinkage int sys_open(const char * filename,int flags,int mode)
{
	char * tmp;
	int error;

	error = getname(filename, &tmp);
	if (error)
		return error;
	error = do_open(tmp,flags,mode);
	putname(tmp);
	return error;
}

下面是do_open的代码。

// 打开一个文件
int do_open(const char * filename,int flags,int mode)
{
	struct inode * inode;
	struct file * f;
	int flag,error,fd;
	// 找到一个可用的文件描述符
	for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++)
		// 还没被使用则找到可用的
		if (!current->files->fd[fd])
			break;
	// 找不到可用的
	if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur)
		return -EMFILE;
	// 清除close_on_exec标记位
	FD_CLR(fd,&current->files->close_on_exec);
	// 获取一个可用的file结构体
	f = get_empty_filp();
	if (!f)
		return -ENFILE;
	// 建立fd到file结构体的映射
	current->files->fd[fd] = f;
	f->f_flags = flag = flags;
	f->f_mode = (flag+1) & O_ACCMODE;
	if (f->f_mode)
		flag++;
	if (flag & (O_TRUNC | O_CREAT))
		flag |= 2;
	// 找到文件对应的inode节点,放到inode变量中
	error = open_namei(filename,flag,mode,&inode,NULL);
	if (!error && (f->f_mode & 2)) {
		error = get_write_access(inode);
		if (error)
			iput(inode);
	}
	if (error) {
		current->files->fd[fd]=NULL;
		f->f_count--;
		return error;
	}
	// 建立file结构体和inode的映射关系
	f->f_inode = inode;
	// 初始化文件偏移
	f->f_pos = 0;
	f->f_reada = 0;
	f->f_op = NULL;
	// 赋值操作file结构体的函数集
	if (inode->i_op)
		f->f_op = inode->i_op->default_file_ops;
	if (f->f_op && f->f_op->open) {
		// 调用底层文件系统的open函数
		error = f->f_op->open(inode,f);
		if (error) {
			if (f->f_mode & 2) put_write_access(inode);
			iput(inode);
			f->f_count--;
			current->files->fd[fd]=NULL;
			return error;
		}
	}
	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
	return (fd);
}

主要工作是通过open_namei找到文件对应的inode节点,然后执行底层的open。最后建立fd到file到inode的关系。在虚拟文件系统中,超级块、inode、目录、文件都是一个抽象的概念,具体的操作函数由各个文件系统实现。所以我们首先找到一个结构体,然后根据实际的操作值调用具体文件系统的操作函数就行。

所以我们先看open_namei,再看具体的open函数。

下面是open_namei的核心代码。

// 找出最后一级目录的inode
	error = dir_namei(pathname,&namelen,&basename,base,&dir);
// 在最后一级目录下找出某文件对应的inode
	error = lookup(dir,basename,namelen,&inode);

大致思路就是先找到最后一级目录的inode,然后在最后一级目录下找某个文件。我们看看怎么找到最后一级目录对应的inode。

// 找出pathname中最后一级目录对应的inode
static int dir_namei(const char * pathname, int * namelen, const char ** name,
	struct inode * base, struct inode ** res_inode)
{
	char c;
	const char * thisname;
	int len,error;
	struct inode * inode;

	*res_inode = NULL;
	// 没有传base则从进程的当前工作目录开始找
	if (!base) {
		base = current->fs->pwd;
		base->i_count++;
	}
	// 绝对路径,则从根目录开始找
	if ((c = *pathname) == '/') {
		iput(base);
		base = current->fs->root;
		pathname++;
		base->i_count++;
	}
	// 找出pathname最后一级目录对应的inode节点
	while (1) {
		thisname = pathname;
		// 以/分割路径部分,遇到/则得到某级路径名称,thisname指向当前路径首地址,len代表该部分路径对应的长度
		for(len=0;(c = *(pathname++))&&(c != '/');len++)
			/* nothing */ ;
		// c为空说明到最后一个字符,即路径结束,不找最后一级的文件或者目录了
		if (!c)
			break;
		base->i_count++;
		// 在base目录下查找某个文件
		error = lookup(base,thisname,len,&inode);
		if (error) {
			iput(base);
			return error;
		}
		// 设置base为inode,继续找
		error = follow_link(base,inode,0,0,&base);
		if (error)
			return error;
	}
	if (!base->i_op || !base->i_op->lookup) {
		iput(base);
		return -ENOTDIR;
	}
	*name = thisname;
	*namelen = len;
	*res_inode = base;
	return 0;
}

就是通过以/分割字符串,在开始的目录下,即根目录或者给定的目录。找到下一级目录的inode,不断地迭代就能找到最后一级目录的inode了。我们发现在查找最后一级目录的inode和在某个目录下找某个文件的inode都用到lookup函数。我们看看他的实现。核心代码。

return dir->i_op->lookup(dir,name,len,result);

以ext文件系统为例。看看lookup的实现。

int ext_lookup(struct inode * dir,const char * name, int len,
	struct inode ** result)
{
	int ino;
	struct ext_dir_entry * de;
	struct buffer_head * bh;

	*result = NULL;
	if (!dir)
		return -ENOENT;
	if (!S_ISDIR(dir->i_mode)) {
		iput(dir);
		return -ENOENT;
	}
	if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) {
		iput(dir);
		return -ENOENT;
	}
	ino = de->inode;
	brelse(bh);
	if (!(*result = iget(dir->i_sb,ino))) {
		iput(dir);
		return -EACCES;
	}
	iput(dir);
	return 0;
}


static struct buffer_head * ext_find_entry(struct inode * dir,
	const char * name, int namelen, struct ext_dir_entry ** res_dir,
	struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir)
{
	long offset;
	struct buffer_head * bh;
	struct ext_dir_entry * de;

	*res_dir = NULL;
	if (!dir)
		return NULL;
#ifdef NO_TRUNCATE
	if (namelen > EXT_NAME_LEN)
		return NULL;
#else
	if (namelen > EXT_NAME_LEN)
		namelen = EXT_NAME_LEN;
#endif
	// 读取目录的文件内容,然后比较
	bh = ext_bread(dir,0,0);
	if (!bh)
		return NULL;
	if (prev_dir)
		*prev_dir = NULL;
	if (next_dir)
		*next_dir = NULL;
	offset = 0;
	de = (struct ext_dir_entry *) bh->b_data;
	while (offset < dir->i_size) {
		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
			brelse(bh);
			bh = NULL;
			bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
			if (!bh)
				continue;
			de = (struct ext_dir_entry *) bh->b_data;
			if (prev_dir)
				*prev_dir = NULL;
		}
		if (de->rec_len < 8 || de->rec_len % 8 != 0 ||
		    de->rec_len < de->name_len + 8 ||
		    (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
			printk ("ext_find_entry: bad dir entry\n");
			printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
				dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
			de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE);
			offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
			continue;
/*			brelse (bh);
			return NULL; */
		}
		// 是否找到了想要的文件
		if (ext_match(namelen,name,de)) {
			*res_dir = de;
			if (next_dir)
				if (offset + de->rec_len < dir->i_size &&
				    ((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data)
					*next_dir = (struct ext_dir_entry *)
						((char *) de + de->rec_len);
				else
					*next_dir = NULL;
			return bh;
		}
		offset += de->rec_len;
		if (prev_dir)
			*prev_dir = de;
		de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
	}
	brelse(bh);
	return NULL;
}

代码比较多,可以不用细究,主要是把目录的文件内容从硬盘读取进来,然后遍历每一个文件名是否和要找的一致就行。

通过上面的分析我们已经找到了一个文件对应的inode节点了。一般文件系统没有实现open函数。所以直接返回inode,建立fd到file到inode的关系即可。后面对文件的操作也是通过inode节点的操作函数集进行。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档