Linux挂载文件系统后挂载点目录项dentry的联系

第一次写博客,水平难免有限,请各位见谅。

最近在看linux内核mount文件系统的相关内容,由此产生了一些疑惑,即在mount一个文件系统后,原有的目录项与被挂载的文件系统的根目录dentry之间是如何联系的,vfs进行路径搜索的时候,是怎么知道该目录项被挂载了一个新的文件系统,新的文件系统又如何与挂载位置的目录项联系起来?通过程序验证后略知一二,特此记录。

本文分析基于linux内核4.4.198。

假设条件

  1. /tmp是一个挂载点
  2. /tmp上只挂载了一个文件系统

相关数据结构

// 查找dentry使用的函数
/**
 * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
 * @dentry:  pointer to dentry of the base directory
 * @mnt: pointer to vfs mount of the base directory
 * @name: pointer to file name
 * @flags: lookup flags
 * @path: pointer to struct path to fill
 */
int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
		    const char *name, unsigned int flags,
		    struct path *path)

//该结构体是struct mount的一部分,一个struct mount实例代表一个挂载点
struct vfsmount {
	struct dentry *mnt_root;	/* root of the mounted tree */
	struct super_block *mnt_sb;	/* pointer to superblock */
	int mnt_flags;
};

//表示路径
struct path {
	struct vfsmount *mnt;
	struct dentry *dentry;
};

// 目录项
struct dentry {
	/* RCU lookup touched fields */
	unsigned int d_flags;		/* protected by d_lock */
	seqcount_t d_seq;		/* per dentry seqlock */
	struct hlist_bl_node d_hash;	/* lookup hash list */
	struct dentry *d_parent;	/* parent directory */
	struct qstr d_name;
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	unsigned char d_iname[DNAME_INLINE_LEN];	/* small names */

	/* Ref lookup also touches following */
	struct lockref d_lockref;	/* per-dentry lock and refcount */
	const struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	unsigned long d_time;		/* used by d_revalidate */
	void *d_fsdata;			/* fs-specific data */

	struct list_head d_lru;		/* LRU list */
	struct list_head d_child;	/* child of parent list */
	struct list_head d_subdirs;	/* our children */
	/*
	 * d_alias and d_rcu can share memory
	 */
	union {
		struct hlist_node d_alias;	/* inode alias list */
	 	struct rcu_head d_rcu;
	} d_u;
};

分析

通过遍历根目录"/“对应dentry(可以通过current->fs->root获得)的d_subdirs链表,能够找到一个名为"tmp"的dentry实例,注意这里的dentry名没有”/",通过该实例的d_flags成员变量可以知道该dentry被标记为"已mounted",但是该dentry不是一个root dentry,即该dentry的d_parent成员变量指向的是根目录"/"对应dentry而不是自身???

不是每个文件系统根目录的dentry的d_parent成员变量都指向自身的吗?继续往下分析–》

通过函数vfs_path_lookup,查找路径"/tmp/kaka",最后一个参数struct path *path被填充,可以通过path->mnt->mnt_root获得目标dentry所在文件系统的根目录,该目录名称为"/",经分析,这个dentry实例与vfs的根目录"/“不是一个东西,每个挂载在vfs上的文件系统,都有一个名为”/"对应的dentry,作为该文件系统的根目录。继续分析,该dentry的d_flags成员变量显示该dentry未被mounted,并且,该dentry是一个root dentry,即该dentry的d_parent成员变量指向的是自身。

原来,上面这个名为"tmp"的dentry和由path得到的名为"/“的dentry其实都代表的是一个路径即”/tmp",vfs查找路径的时候,通过根目录找到的名为"tmp"的dentry,会检查他的d_flags成员变量,发现该dentry为mmounted状态的话,会继续查找究竟是谁挂载在"tmp"这个目录项上了,找到后再继续往下找。

那么,究竟内核是怎么查找到名为"tmp"的dentry实例和由path得到的名为"/"的dentry实例直接存在关系的呢?通过查看struct dentry的定义,好像并没有找到一个成员变量能过将这两个实例联系在一起???还有,内核查找路径不是通过dentry结构的d_subdirs成员变量链表进行查找的吗?

继续分析发现,linux内核是通过二元组{dentry,mount}找到当前路径下的相关内容,或者通过look_up回调函数进行硬件读取查找;其中对于第一种方法,通过二元组{dentry,mount}获取相关hash结果,在对应散列表下搜索。对于上面的问题,内核代码vfs_path_lookup->filename_lookup->path_lookupat->link_path_walk->walk_component->lookup_fast->__follow_mount_rcu函数中有如下代码:

if (!d_mountpoint(path->dentry))
			return !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);

		mounted = __lookup_mnt(path->mnt, path->dentry);

/*
 * find the first mount at @dentry on vfsmount @mnt.
 * call under rcu_read_lock()
 */
struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
{
	struct hlist_head *head = m_hash(mnt, dentry);
	struct mount *p;

	hlist_for_each_entry_rcu(p, head, mnt_hash)
		if (&p->mnt_parent->mnt == mnt && p->mnt_mountpoint == dentry)
			return p;
	return NULL;
}

通过__lookup_mnt函数使得上述问题的两个dentry关联了起来。

值得一提的是,在函数vfs_path_lookup()中path变量作为一个buffer般的存在存储着中间变量,并被多次修改。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
教学内容与要求   1掌握处理器在进程地址空间上的三种运行位置,了解内核编程不能使用C库函数和FPU,以及可能产生内存故障、核心栈溢出和四种内核竞争情形的原因。(2学时)   2熟悉进程描述符的组织,进程上下文和进程状态转换,和fork,exec,wait,exit,clone,linux线程和内核线程的实现原理和应用。了解COW和避免出现孤儿进程技术。(4小时)   3介绍支持SMP的O(1)调度,用户和内核抢占和进程上下文切换,了解优先级复算,睡眠和唤醒机制,SMP的负载均衡。(4小时)   4掌握在x86体系结构上系统调用的具体实现原理,接口参数传递,用户地址空间和核心地址空间之间的数据传输,和增加新的系统功能的方法。(2小时)   5熟悉在x86体系结构上Linux中断和异常的处理原理,中断注册、共享、控制,和中断上下文的意义,中断和设备驱动程序的关系,以及设备驱动程序结构和用户接口。(4小时)   6中断处理程序被分解为top half和bottom half的原因,介绍linux的softirq,tasklet,ksoftirqd和work queue,分析进程与top half,bottom half的竞争情形和同步。(4小时)   7掌握内核同步原理和方法:原子操作,自旋锁,(读—写)信号量,完成变量,bkl,seqlock和延迟内核抢占。了解指令“路障”。(4小时)   8介绍系统时钟和硬件定时器,单处理器和多处理器上的linux计时体系结构,定时的时间插补原理,单处理器和多处理器上的时钟中断处理,动态定时器的数据结构和算法原理,定时器竞争情形,延迟函数。Time,gettimeofday,adjtimex,setitimer,alarm的实现原理和应用。(4小时)   9熟悉进程地址空间的区和页,分配和释放物理页,物理地址与逻辑地址、虚地址之间的映射,slub分配原理和方法,高端物理内存的映射。(4小时)   10介绍VFS原理,超级块,inode结构和方法,dentry结构和方法,file结构和方法,以及进程打开文件表,linux中的文件系统。(2小时)   11讲解块设备缓冲,bio结构,I/O请求队列,和有最终期限的块I/O调度算法。(2小时)   12熟悉进程地址空间的分区,mm_struct结构,vm_area_struct结构和操作,,进程的页表文件映射接口mmap原理和方法。(2小时)   13熟悉页cache和radix_tree,缓冲区cache,和pdflush内核线程原理。(2小时)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值