文件系统学习3 注册和加载 以及路径解析

本文详细解析了Linux文件系统的注册与加载过程,重点介绍了sys_mount系统调用的内部实现,包括路径解析、文件系统类型获取、超级块读取与解析等关键步骤。此外,还深入探讨了文件系统的挂载关系及mount操作的具体流程。

文件系统注册
注册fs:国家允许开银行/国企/股份制公司,把他们的性质特征写在法律里面公开,大家可以查阅。=>注册到链表
加载fs:开工商银行,神华公司等。需要登记公司地址和联系方式(mnt),法人(sb)等信息。=>同样注册各种链表,包括更追溯到上一级公司,下面的子公司等。

module_init(init_ext3_fs)
	=>init_ext3_xattr();
	=>init_inodecache();
	=>register_filesystem(&ext3_fs_type);//如果在链表则返回,否则加入链表
		static struct file_system_type ext3_fs_type = {
			.owner		= THIS_MODULE,
			.name		= "ext3",
			.get_sb		= ext3_get_sb,
			.kill_sb	= kill_block_super,
			.fs_flags	= FS_REQUIRES_DEV,
		};

加载,共有两个路径,都需要解析

sys_mount
	=>retval = do_mount((char *)dev_page, dir_page, (char *)type_page, flags, (void *)data_page);
		=>retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
			=>do_path_lookup(AT_FDCWD, name, flags, nd);//加载的路径解析
				=>if (*name=='/')
					==>nd->mnt = mntget(fs->rootmnt);
					==>nd->dentry = dget(fs->root);//初始化
				=>else if (dfd == AT_FDCWD)
					==>nd->mnt = mntget(fs->pwdmnt);
					==>nd->dentry = dget(fs->pwd);
				=>retval = path_walk(name, nd);							}
		=>retval = do_new_mount(&nd, type_page, flags, mnt_flags, dev_name, data_page);
			=>struct vfsmount *mnt = do_kern_mount(type, flags, name, data);
				=>struct file_system_type *type = get_fs_type(fstype);
				=>struct vfsmount *mnt = vfs_kern_mount(type, flags, name, data);//3大功能 分配mnt;分配sb(如果没有分配的话);填充mnt基本信息
					=>mnt = alloc_vfsmnt(name);//第一功能
					=>error = type->get_sb(type, flags, name, data, mnt);//Super.c (c:\linux\linux-2.6.23\fs\ext2):	.get_sb		= ext2_get_sb,//第二功能
						=>ext2_get_sb
							=>get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super, mnt);//到了通用通用块层
								=>bdev = open_bdev_excl(dev_name, flags, fs_type);//需要通过块设备驱动读取的原因是,超级块不在文件系统管理之内,文件系统的root_inode等信息存放在裸块里面,只能通过块设备驱动先读取块信息,然后根据文件系统type解析里面的内容填充到root_inode
									=>struct block_device *bdev = lookup_bdev(path);
										=>error = path_lookup(path, LOOKUP_FOLLOW, &nd);
											=>do_path_lookup(AT_FDCWD, name, flags, nd);//设备节点路径解析
										=>inode = nd.dentry->d_inode;
										=>bdev = bd_acquire(inode);
									=>error = blkdev_get(bdev, mode, 0);
										=>__blkdev_get(bdev, mode, flags, 0);
											=>do_open(bdev, &fake_file, for_part);//建立bdev、block_device和gendisk的联系												
								=>s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
								=>if (s->s_root)
									close_bdev_excl(bdev);//如果已经有super block,那么不需要重新申请和填充
								=>else
									sb_set_blocksize(s, block_size(bdev));
									error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);//调用ext2_fill_super回调函数
										=>sb_block = get_sb_block(&data);//默认是1,如果mount参数有sb=xxx则用用户参数,extx文件系统超级块有备份,默认超级块故障可以用备份超级块mount进行恢复
										=>sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);//分配超级快
										sb->s_fs_info = sbi;
										=>blocksize = sb_min_blocksize(sb, BLOCK_SIZE);//估算最小逻辑块大小,为读取超级块做准备
										=>bh = sb_bread(sb, logic_sb_block)//读取超级块,结果放在bh磁盘高速缓存里面
										=>es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);//解析ext2磁盘超级块,并将高速缓存的sb信息填充到sbi里面
										sbi->s_es = es;
										sb->s_magic = le16_to_cpu(es->s_magic);
										=>for (i = 0; i < db_count; i++)
											block = descriptor_loc(sb, logic_sb_block, i);
											sbi->s_group_desc[i] = sb_bread(sb, block);
										=>sb->s_op = &ext2_sops;//赋值超级块的方法
										sb->s_export_op = &ext2_export_ops;
										=>ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY);
					=>mnt->mnt_mountpoint = mnt->mnt_root;//第三功能
					mnt->mnt_parent = mnt;
			=>do_add_mount(mnt, nd, mnt_flags, NULL);//将mnt放到对应的一坨各种链表里面
				=>while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry));//抽丝剥茧,找到堆叠的最底层的dentry,详见《mount过程分析之七(do_add_mount)》
				=>err = graft_tree(newmnt, nd))//加入链表

全路径查找是核心

static int fastcall path_walk(const char * name, struct nameidata *nd)
	=>link_path_walk(name, nd);
		=>result = __link_path_walk(name, nd);
			=>inode = nd->dentry->d_inode;
			=>for(;;) //每一次循环,剥离一层目录,通过hash抽丝剥茧找到以此找到下一个entry
				==>this.name = name;//初始化hash和剥离出/~/之间的第一个name字符串,指向起始地址,通过len决定长度
				c = *(const unsigned char *)name;
		
				hash = init_name_hash();
				do {
					name++;
					hash = partial_name_hash(c, hash);
					c = *(const unsigned char *)name;
				} while (c && (c != '/'));
				this.len = name - (const char *) this.name;//this.len等于两个/之间的长度,即一个目录
				this.hash = end_name_hash(hash);
				==>nd->dentry->d_op->d_hash(nd->dentry, &this);//如果fs有私有hash,则选用私有hash,这个是dentry的方法
				==>do_lookup(nd, &this, &next);
					=>struct dentry *dentry = __d_lookup(nd->dentry, name);
					=>dentry = real_lookup(nd->dentry, name, nd);
						=>result = d_lookup(parent, name);
							=>struct hlist_head *head = d_hash(parent,hash);
							=>hlist_for_each_entry_rcu(dentry, node, head, d_hash)//命中哈希后,在哈希的链表里面遍历
								==>qstr = &dentry->d_name;//匹配entry,如果有私有的方法,则用私有,这个是dentry的方法,否则字符串匹配
								if (parent->d_op && parent->d_op->d_compare) {
									if (parent->d_op->d_compare(parent, qstr, name))
										goto next;
								} else {
									if (qstr->len != len)
										goto next;
									if (memcmp(qstr->name, str, len))
										goto next;
								}
								==>found = dentry;//找到则返回
						=>if (!result)//如果缓存没有找到,则需要从磁盘高速缓存读取
							==>result = dir->i_op->lookup(dir, dentry, nd);//inode的方法
								=>ext2_lookup
									=>ino = ext2_inode_by_name(dir, dentry);
										=>struct ext2_dir_entry_2 * de = ext2_find_entry (dir, dentry, &page);
											=>do {
													page = ext2_get_page(dir, n);
														=>page = read_mapping_page(mapping, n, NULL);
															=>filler_t *filler = (filler_t *)mapping->a_ops->readpage;
															=>return read_cache_page(mapping, index, filler, data);
																=>page = read_cache_page_async(mapping, index, filler, data);
																	=>page = __read_cache_page(mapping, index, filler, data);
																		=>page = find_get_page(mapping, index);//读磁盘高速缓存,否则分配page,通过fill回调函数读取磁盘刷新page
																		=>if (!page)
																			if (!cached_page) 
																				cached_page = page_cache_alloc_cold(mapping);
																		=>page = cached_page;
																		=>err = filler(data, page);
																	=>err = filler(data, page);
											}//do
										=>inode = iget(dir->i_sb, ino);
											=>sb->s_op->read_inode(inode);//.read_inode	= ext2_read_inode,
												=>struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);//高速缓存读取ext2_inode节点
												=>将读取的raw_inode信息赋值给inode内存模型
										=>d_splice_alias(inode, dentry);
				==>if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link)
					====>err = do_follow_link(&next, nd);
					====>inode = nd->dentry->d_inode;
				==>else
					====>path_to_nameidata(&next, nd);//为下一次for循环的do_lookup做准备
						=>nd->mnt = path->mnt;
						nd->dentry = path->dentry;
				==>continue;
last_component://路径最后一个元素
				/* Clear LOOKUP_CONTINUE iff it was previously unset */
				==>nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;	
				==>err = do_lookup(nd, &this, &next);
				==>inode = next.dentry->d_inode;
				==>path_to_nameidata(&next, nd);
				==>return 0;			

文件系统错误修复实例(1)
http://blog.youkuaiyun.com/ChuiGeDaQiQiu/article/details/24138875

卸载文件系统失败的时候可以通过fuser检查和杀死访问磁盘的进程

fuser命令详解(原创)
http://czmmiao.iteye.com/blog/1733722

e2fsprogs编译问题
http://blog.youkuaiyun.com/sanwenyublog/article/details/52817796

mount过程分析之六——挂载关系(图解) 作者的系列博客写得挺好
http://blog.youkuaiyun.com/ZR_Lang/article/details/40343899

mount过程分析之七(do_add_mount)
http://blog.youkuaiyun.com/ZR_Lang/article/details/40325241

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值