Linux VFS V

一个文件系统获得了内核的支持,仅仅是功能上的可能性,如果要使用,还需要将其mount到rootfs下,才能可见并进而被使用,本文主要讨论mount的实现过程。

SYSCALL_DEFINE5(mount...)	//namespace.c
	do_mount()
		user_path()
		do_remount()
		do_loopback()
		do_change_type()
		do_move_mount()
		do_new_mount()
			struct file_system_type *type
			struct vfs_mount *mnt
			mnt = vfs_kern_mount()
				struct mount * mnt
				mnt = alloc_vfsmnt()
				root = mount_fs()
					struct super_block *sb
					root = type->mount()		//回调file_system_type的mount方法
					sb = root ->d_sb
					security_sb_kern_mount()
					up_write()
				init mnt
				list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts)
			do_add_mount(real_mount(mnt),path,mnt_flags)
ext4_mnt()		//ext4
	mount_bdev(...ext4_fill_super)
		struct block_device *bdev
		struct super_block *s
		bdev = blkdev_get_by_path()
		s = sget(...test_bdev_super,set_bdev_super...)		//find or create a superblock
			alloc_super()
				kzalloc()
				init_waitqueue_head()
				s->s_bdi = &noop_backing_dev_info
				...other initialization of s
			set()							//set_bdev_super()
				s->s_bdev = data;
				s->s_dev = s->s_bdev->bd_dev;
				s->s_bdi = &bdev_get_queue(s->s_bdev)->backing_dev_info;
					bdev->bd_disk->queue;
			list_add_tail(...&super_blocks)
			hlist_add_head()
			get_filesystem()
				__module_get()
		sb_set_blocksize()
		fill_super()						//ext4_fill_super()
			struct ext4_sb_info *sbi
			sbi = kzalloc()
			... init sbi...
			ext4_msg()
			setup_timer()
			sb->s_op = &ext4_sops
			sb->s_export_op = &ext4_export_ops
			sb->s_xattr = ext4_xattr_handlers
			sb->s_root = d_make_root()
			ext4_setup_super()
			ext4_ext_init()
			ext4_mb_init()
			sbi->s_kobj.kset = ext4_kset
			init_completion()
			kobject_init_and_add(&sbi->s_kobj, &ext4_ktype...)
		s->s_flags|=MS_ACTIVE
		bdev->bd_super = s
		dget()

bdev文件系统在其初始化时就已经挂载到内核。

bdev_mount()		//bdev	
	mount_pseudo(...&bdev_sops...)
sysfs_mount()		//sysfs
	struct dentry* root
	void * ns
	ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
	root = kernfs_mount_ns

Linux VFS IV

在Linux内核中,一个文件系统包括两部分,一部分是位于磁盘上的管理数据,即文件系统的本体,另一部分是位于内核中的代码,即对VFS中相应接口的实现,没有这部分实现,内核无法识别该文件系统,如此mount、读写等操作也就无从谈起了。

向内核中添加一个文件系统的支持,与其实例无关,即两个分区都是ext4文件系统,但内核对于ext4的支持的代码只需要执行一次,而且,此时也没有从磁盘读取任何数据,这是mount的工作。

本文主要整理内核对各个文件系统的初始化代码

devtmpfs

//devtmpfs
__init devtmpfs_init()		//drivers/base/devtmpfs.c
	register_filesystem($dev_fs_type)
	thread = kthread_run()

sysfs

sysfs_init()		//fs/sysfs/mount.c
	sysfs_root = kernfs_create_root()
	sysfs_root_kn = sysfs_root->kn;
	register_filesystem(&sysfs_fs_type);

proc

proc_root_init()	//fs/proc/root.c
	proc_init_inodecache()
	register_file_system(&proc_fs_type)
	proc_self_init()
	proc_thread_self_init()
	proc_net_init()
	proc_mkdir("fs)
	proc_mkdir("driver")
	proc_mkdir("fs/nfsd")
	proc_tty_init()
	proc_mkdir("bus")
	proc_sys_init()

ext4

__init ext4_init_fs()			//fs/ext4/super.c
	ext4_init_es()
	ext4_init_pageio()
	ext4_init_system_zone()
	kset_create_and_add()
	proc_mkdir("fs/ext4")
	init_inodecache()
	register_filesystem(&ext4_fs_type)	//fs/filesystems.c
		find_filesystem()			
			for (p=&file_systems; *p; p=&(*p)->next)...

bdev

注意到bdev文件系统在初始化时已经挂载

static struct file_system_type bd_type = {
        .name           = "bdev",
        .mount          = bd_mount,
        .kill_sb        = kill_anon_super,
};

__init bdev_cache_init()				//fs/block_dev.c
	struct vfsmount * bd_mnt
	bdev_cachep = kmem_cache_create("bdev_cache")
	register_filesystem(&bd_type)
	bd_mnt = kern_mount(&bd_type)
		kern_mount_data()
			vfs_kern_mount()
				mnt = alloc_vfsmnt(name)
				root = mount_fs(type, flags, name, data);
					 root = type->mount()		//bd_mount	
					 sb = root->d_sb
				mnt->mnt = ...
				list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
			real_mount()->mnt_ns = MNT_NS_INTERNAL
	blockdev_superblock = bd_mnt->mnt_sb	//for writeback

Linux VFS III

open()的最终目的是:准备好file以及依赖的数据结构。为此,open()主要做了以下几方面工作:

  1. PathWalk找到目标文件
  2. 构造并初始化inode
  3. 构造并初始化file

其中,PathWalk已经讨论过,本文主要聚焦inode和file的构造和初始化


SYSCALL_DEFINE3(open...)	//open.c +1038
	do_sys_open()
		build_open_flags()
		struct filename *tmp = getname()
			getname_flags()
				kname = (char*)result + sizeof(*result)
				result->name = kname
				strncpy_from_user(kname, filename,max)
				result->uptr = filename
		fd = get_unused_fd_flags()
			__alloc_fd()
		struct file *f = do_filp_open()
			struct nameidata nd
			path_openat()
				file = get_empty_filp()
				file->f_flags = op->open_flag
				path_init()
					link_path_walk()
						may_lookup()
						walk_component()
							handle_dots()
							lookup_fast()	
							lookup_slow()	//
								__lookup_hash()
									lookup_dcache()
									lookup_real()
										dir->i_op->lookup()	//ext4_lookup
											inode = ext4_iget_normal()
												ext4_iget()
													struct ext4_inode * raw_inode
													struct inode *inode
													inode = iget_locked()
														inode = find_inode_fast()
														inode = alloc_inode()
														inode->i_ino = ino
														inode->i_state = I_NEW
														hlist_add_head()
														inode_sb_list_add()
													__ext4_get_inode_loc()
														stuct buffer_head *bh
														struct ext4_group_desc *gdp
														ext4_inode_table()
														iloc->block_group = ...
														iloc->offset = ...
														get_bh(bh)
														bh->b_endio = end_buffer_read_sync
														submit_bh*()
															submit_bio()
														wait_on_buffer()
														iloc->bh = ...
													raw_inode = ext4_raw_inode()
													inode->i_blocks = ext4_inode_blocks()
													inode->isize = ext4_isize()
													inode->i_op=...
													inode->i_fop= ...
							inode = path->dentry->d_inode
							nd->inode = inode
				do_last()
					handle_dots()
					lookup_fast()
					complete_walk()
						dentry->d_op->d_weak_revalidate()
					lookup_open()
						struct dentry *dir = nd->path.dentry
						struct inode *dir_inode = dir->d_inode
						lookup_dcache()
						atomic_open()
						lookup_real()
						vfs_create()
					audit_inode()
					mnt_want_write()
					may_open()
					vfs_open()
						struct inode *inode = path->dentry->d_inode
						inode->i_op->dentry_open()
						do_dentry_open()
							inode = f->f_inode = f->f_path.dentry->d_inode
							f->f_mapping = inode->i_mapping
							f->f_op=fops_get(inode->i_fop)
							open = f->f_op->open
							open(inode,f)
					terminate_walk()
		fsnotity_open()

Linux VFS II

PathWalk,顾名思义,就是根据路径找到目标的文件或目录,作为每次open()或opendir()都要执行的过程,其重要性不言而喻。

PathWalk中的很重要的一个概念就是nameidata,这个对象可以看作是PathWalk过程的上下文的封装,记录当前查找所用的路径,如果查找结束,则记录最终的目标路径。作为”上下文”,其主要工作就是封装了在一个过程中各个子逻辑都需要的信息,并随着过程的进行贯穿于各个子过程供其在需要时使用。

在内核中,PathWalk的主要逻辑整理如下


path_init(name, nd)		//根据name初始化nd
lookup_last(nd, &path)	//此时path还是空的
	walk_component()
		handle_dots()
		lookup_fast(nd, path, &inode)
			__d_lookup_rcu()
			struct vfsmount *mnt = nd->path.mnt
			path->mnt = mnt
			path->dentry = dentry
			__follow_mount_rcu(nd, path, inode)
				mounted = __lookup_mnt()
				path->mnt = &mounted->mnt
				//用下溯文件系统的mounted来更新path
				path->dentry = mounted->mnt.mnt_root;
				return 0 ;	//不需look_up时
				return 1 ; 	//仍需look_up时
		lookup_slow()
			__lookup_hash()
				lookup_dcache()
					d_lookup()
					d_alloc()
				lookup_real()
			path->mnt = nd->path.mnt
			path->dentry = dentry
			follow_managed()
		path_to_nameidate(path,nd)	//根据新path设置nameidata
			nd->path.mnt = path->mnt
		nd->inode = inode

这段逻辑使用上下文nameidata对象作为PathWalk过程中局部的全局变量,实现递归的目录搜索。

Linux VFS I

为了实现VFS的设计目的 – 抽象出文件系统的共性,作为抽象,VFS不但打通了上下的通道,还打通了文件系统之间的通道,就像当下流行的”云”,将各种设备(文件系统)和系统联系到一起。在VFS中,有6个关键类:inode,dentry,file,super_block,vfs_mount和file_system_type,整个VFS”大厦”的建造都是围绕这6个”支柱”的。

这几个数据结构的实现都**“include/linux/fs.h”**中,下面这张图简要的描述了本文介绍这几个对象及其关系

super_block

–1326–>s_blocksize_bits;当块大于256byte时,块大小是2的多少次幂,比如块大小是512时,就是9
–1327–>s_blocksize;块的大小,这两个域参见sb_set_blocksize()
–1328–>file_system_type对象指针,该super_block对象表示的具体的文件系统类型,内核使用指针链表管理所有的file_system_type对象,其中有将一个mount一个文件系统回调接口
–1330–>和inode,file,dentry等一样, super_block”类”也封装了自己的操作方法集
–1334–>这个文件系统挂载到根目录时的flag,比如MS_RDONLY表示只读挂载etc。同样定义在fs.h
–1336–>不同文件系统的魔幻数,比如ramfs的RAMFS_MAGIC,ext4的EXT4_SUPER_MAGIC,定义在“include/linux/magic.h”
–1337–>文件系统的根目录,即mount一个目录到目录树时,该文件系统的挂载点在目录树的dentry对象
–1348–>使用NFS时的导出dentry
–1350–>文件系统的block_device对象指针
–1351–>文件系统的backing_dev_info对象指针
–1352–>MTD设备的。。。
–1362–>文件系统私有数据
–1380–>
–1427–>文件系统stack的深度
–1431–>该super_block管理的所有的inode对象链表
–1434–>该super_block管理的所有的回写inode对象链表

实际磁盘的文件都是由文件系统管理的,文件系统挂载到VFS上之后,内核想要从磁盘上获取这个文件,就要按照事前的约定,从磁盘头读取相应文件系统的信息,比如类型,分区,校验码等,叫做文件系统的super block信息,可以看作是整个文件系统的元数据的容器,只有读取到super block,内核才能正确的进行接下来的操作。对于ext4文件系统,super block按照内核struct ext4_super_block的格式存储在相应分区,内核使用struct ext4_super_block描述该super block信息并进一步将其封装到VFS super_block中。

inode

Continue reading