Linux1.0虚拟文件系统(VFS)

Linux1.0虚拟文件系统(VFS)

在linux1.0操作系统当中文件系统是非常重要的一环,Linux操作系统当中支持的文件系统众多,如ext,ext2,minix,iosfs等等。当我们在使用操作系统提供的文件读写api来操作系统中的文件的时候,操作系统内部是怎么去完成我们想要的功能呢?在操作系统当中设计了一种虚拟文件系统,就是将所有的文件系统都给抽象出来,形成不同的文件系统使用相同的api来完成相同功能。Linux1.0中的文件系统结构可以用下面的结构来表示: 


在实现这种VFS结构当中,操作系统当中定义了几个非常重要的结构:

struct file {
	mode_t f_mode;
	dev_t f_rdev;			/* needed for /dev/tty */
	off_t f_pos;
	unsigned short f_flags;
	unsigned short f_count;  /*文件的引用计数*/
	unsigned short f_reada;
	struct file *f_next, *f_prev;
	struct inode * f_inode;
	struct file_operations * f_op;
};

struct file_operations {
	int (*lseek) (struct inode *, struct file *, off_t, int);
	int (*read) (struct inode *, struct file *, char *, int);
	int (*write) (struct inode *, struct file *, char *, int);
	int (*readdir) (struct inode *, struct file *, struct dirent *, int);
	int (*select) (struct inode *, struct file *, int, select_table *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	int (*mmap) (struct inode *, struct file *, unsigned long, size_t, int, unsigned long);
	int (*open) (struct inode *, struct file *);
	void (*release) (struct inode *, struct file *);
	int (*fsync) (struct inode *, struct file *);
};

struct inode {
	dev_t		i_dev;			/* 设备号 */
	unsigned long	i_ino;		/* i节点号 */
	umode_t		i_mode;
	nlink_t		i_nlink;
	uid_t		i_uid;           /*创建文件的用户ID*/
	gid_t		i_gid;			 /*创建文件的用户组ID*/
	dev_t		i_rdev;
	off_t		i_size;
	time_t		i_atime;
	time_t		i_mtime;
	time_t		i_ctime;
	unsigned long	i_blksize;
	unsigned long	i_blocks;
	struct semaphore i_sem;
	struct inode_operations * i_op;
	struct super_block * i_sb;      /*对应的超级块*/
	struct wait_queue * i_wait;
	struct file_lock * i_flock;
	struct vm_area_struct * i_mmap; /* 该文件映射到的虚拟地址段的地址 */
	struct inode * i_next, * i_prev;     /*空闲双向链表*/
	struct inode * i_hash_next, * i_hash_prev; /*hash双向链表*/
	struct inode * i_bound_to, * i_bound_by;
	struct inode * i_mount;
	struct socket * i_socket;  /*如果是网络inode,则指向网络数据*/
	unsigned short i_count;
	unsigned short i_flags;
	unsigned char i_lock;
	unsigned char i_dirt;
	unsigned char i_pipe;
	unsigned char i_seek;
	unsigned char i_update;
	/* 因为Linux采用的时VFS文件系统,上面数据是所有文件系统都需要使用的数据 
	 * 下面的数据是相应文件系统对应的特定inode的信息
	 */
	union {
		struct pipe_inode_info pipe_i;
		struct minix_inode_info minix_i;
		struct ext_inode_info ext_i;
		struct ext2_inode_info ext2_i;
		struct hpfs_inode_info hpfs_i;
		struct msdos_inode_info msdos_i;
		struct iso_inode_info isofs_i;
		struct nfs_inode_info nfs_i;
		struct xiafs_inode_info xiafs_i;
		struct sysv_inode_info sysv_i;
	} u;
};

struct inode_operations {
	struct file_operations * default_file_ops;
	int (*create) (struct inode *,const char *,int,int,struct inode **);
	int (*lookup) (struct inode *,const char *,int,struct inode **);
	int (*link) (struct inode *,struct inode *,const char *,int);
	int (*unlink) (struct inode *,const char *,int);
	int (*symlink) (struct inode *,const char *,int,const char *);
	int (*mkdir) (struct inode *,const char *,int,int);
	int (*rmdir) (struct inode *,const char *,int);
	int (*mknod) (struct inode *,const char *,int,int,int);
	int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);
	int (*readlink) (struct inode *,char *,int);
	int (*follow_link) (struct inode *,struct inode *,int,int,struct inode **);
	int (*bmap) (struct inode *,int);
	void (*truncate) (struct inode *);
	int (*permission) (struct inode *, int);
};

就是以上四种结构体 struct file、struct file_operations、struct inode、struct inode_operations。struct file结构体是系统中对所有文件系统的文件的描述,包含文件的基本信息,但是虽然包含了文件的基本信息,但是你需要对文件进行操作啊,所以这里就是问题的重点来了。所有的文件系统都需要有open、close、read、write操作,但是不同的文件系统的具体实现是不一样的,也就是同样打开一个文件,不同的文件系统中具有不同的操作,在VFS中将这些需要去操作具体文件系统的函数给抽象出来,形成一个struct file_operations,此时你回来去看看struct file_operations这个结构体,你会发现这个结构体当中有lseek、read、wirte、open等函数,当你调用一个open系统调用时,最后其实是struct file_operations中的open函数去完成的。同样的道理struct inode是描述文件在磁盘上信息的一个结构体,里面信息非常多。在struct inode中除了一些inode的基本信息之外,还有一个很特别的联合体,该联合体是用来描述不同文件系统的特定inode的信息。有兴趣的话,可以自己去看看,我这里就不贴上代码了。struct inode记录了文件磁盘的基本信息,同样的一个问题就是这些信息也是需要被操作和修改的啊,所以也有一个struct inode_operations结构,你可以回头去看看这些结构的信息。

下面就以系统当中open函数为例来说明VFS的一个基本实现过程

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++)
		if (!current->filp[fd])
			break;
	if (fd>=NR_OPEN)
		return -EMFILE;
	FD_CLR(fd,¤t->close_on_exec);
	f = get_empty_filp();
	if (!f)
		return -ENFILE;
	current->filp[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;
	/* 通过一个路径filename来打开一个文件,并获取文件的inode
	 */
	error = open_namei(filename,flag,mode,&inode,NULL);
	if (error) {
		current->filp[fd]=NULL;
		f->f_count--;
		return error;
	}

	/* 在open函数当中,先通过open_namei函数获取对应路径文件的inode
	 * 在获取的inode当中会有f_op的操作符,Linux的文件系统是根据所要
	 * 操作文件的类型来确定文件操作的f_op和i_op,因为Linux支持的文件系统众多
	 * 每种文件系统的f_op和i_op都有特定的实现,文件系统具体函数的实现在相应的
	 * 文件夹当中,如ext2文件系统的实现在/fs/ext2/当中
	 */
	f->f_inode = inode;
	f->f_pos = 0;
	f->f_reada = 0;
	f->f_op = NULL;
	if (inode->i_op)
		f->f_op = inode->i_op->default_file_ops;
	if (f->f_op && f->f_op->open) {
		error = f->f_op->open(inode,f);
		if (error) {
			iput(inode);
			f->f_count--;
			current->filp[fd]=NULL;
			return error;
		}
	}
	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
	return (fd);
}

/* flag为打开文件方式,如可读,可写,可读写,
 * 如果文件没有则会去创建一个新的文件
 * mode中有在创建新的文件时候才起作用,也就是确定新建文件的权限
 */
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;
}

在sys_open函数当中先是做了数据的拷贝,这个现在可以不用管,然后就直接调用了do_open函数,在do_open函数当中首先给分配了一个struct file结构,然后根据函数当中给定的路径来获取相应文件的struct inode,open_namei这个函数我这里来简单介绍一下它的基本过程,首先它会根据路径来查找相应文件的struct inode,依次处理目录中的每一个struct inode,直到找到最后路径给出的那个文件的struct inode,如现在给出下面一个路径/etc/demo.txt,首先会获取/路径的struct inode,然后再该inode下面查找一个叫做etc目录的struct inode,找到了之后再去查找etc这个目录的inode下面的一个叫做demo.txt的struct inode,注意在这个查找的过程当中有一个非常重要的动作,就是给struct inode的i_op赋值,因为在查找的过程当中就知道demo.txt是一个什么文件系统的什么类型的文件了,这个读取inode的函数如下:

void ext2_read_inode (struct inode * inode)
{
	struct buffer_head * bh;
	struct ext2_inode * raw_inode;
	unsigned long block_group;
	unsigned long group_desc;
	unsigned long desc;
	unsigned long block;
	struct ext2_group_desc * gdp;

	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
	     inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
		ext2_error (inode->i_sb, "ext2_read_inode",
			    "bad inode number: %lu", inode->i_ino);
		return;
	}
	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
	if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
		ext2_panic (inode->i_sb, "ext2_read_inode",
			    "group >= groups count");
	group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
	desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
	bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
	if (!bh)
		ext2_panic (inode->i_sb, "ext2_read_inode",
			    "Descriptor not loaded");
	gdp = (struct ext2_group_desc *) bh->b_data;
	block = gdp[desc].bg_inode_table +
		(((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
		 / EXT2_INODES_PER_BLOCK(inode->i_sb));
	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
		ext2_panic (inode->i_sb, "ext2_read_inode",
			    "unable to read i-node block\n"
			    "inode=%lu, block=%lu", inode->i_ino, block);
	raw_inode = ((struct ext2_inode *) bh->b_data) +
		(inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
	/* 浠庣鐩樹腑璇诲彇鐨別xt2鏂囦欢绯荤粺涓枃浠剁殑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_links_count;
	inode->i_size = raw_inode->i_size;
	inode->i_atime = raw_inode->i_atime;
	inode->i_ctime = raw_inode->i_ctime;
	inode->i_mtime = raw_inode->i_mtime;
	inode->u.ext2_i.i_dtime = raw_inode->i_dtime;
	inode->i_blksize = inode->i_sb->s_blocksize;
	inode->i_blocks = raw_inode->i_blocks;
	inode->u.ext2_i.i_flags = raw_inode->i_flags;
	inode->u.ext2_i.i_faddr = raw_inode->i_faddr;
	inode->u.ext2_i.i_frag = raw_inode->i_frag;
	inode->u.ext2_i.i_fsize = raw_inode->i_fsize;
	inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl;
	inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl;
	inode->u.ext2_i.i_version = raw_inode->i_version;
	inode->u.ext2_i.i_block_group = block_group;
	inode->u.ext2_i.i_next_alloc_block = 0;
	inode->u.ext2_i.i_next_alloc_goal = 0;
	if (inode->u.ext2_i.i_prealloc_count)
		ext2_error (inode->i_sb, "ext2_read_inode",
			    "New inode has non-zero prealloc count!");
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
		inode->i_rdev = raw_inode->i_block[0];
	else for (block = 0; block < EXT2_N_BLOCKS; block++)
		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
	brelse (bh);
	inode->i_op = NULL;
	/* 閫氳繃鍒ゆ柇鏂囦欢绫诲瀷鏉ヨ缃枃浠剁殑璇诲啓鍑芥暟
	 */
	if (inode->i_ino == EXT2_ACL_IDX_INO ||
	    inode->i_ino == EXT2_ACL_DATA_INO)
		/* Nothing to do */ ;
	else if (S_ISREG(inode->i_mode))
		inode->i_op = &ext2_file_inode_operations;
	else if (S_ISDIR(inode->i_mode))
		inode->i_op = &ext2_dir_inode_operations;
	else if (S_ISLNK(inode->i_mode))
		inode->i_op = &ext2_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);
	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
		inode->i_flags |= MS_SYNC;
}

在知道了struct file的f_op和struct inode的i_op之后,当你需要去调用某个系统调用的时候,其实最后的关键操作都交给相应的f_op和i_op去操作了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值