Linux内核源代码情景分析-文件系统安装后的访问

Linux内核源代码情景分析-文件系统的安装,一文中,已经调用sudo mount -t ext2 /dev/sdb1 /mnt/sdb,在/mnt/sdb节点上挂载了文件系统,那么我们接下来访问/mnt/sdb/hello.c节点。我们来看一下path_walk的执行有什么不同?

int path_walk(const char * name, struct nameidata *nd)
{
	struct dentry *dentry;
	struct inode *inode;
	int err;
	unsigned int lookup_flags = nd->flags;

	while (*name==‘/‘)
		name++;
	if (!*name)
		goto return_base;

	inode = nd->dentry->d_inode;
	if (current->link_count)
		lookup_flags = LOOKUP_FOLLOW;

	/* At this point we know we have a real path component. */
	for(;;) {
		unsigned long hash;
		struct qstr this;
		unsigned int c;

		err = permission(inode, MAY_EXEC);
		dentry = ERR_PTR(err);
 		if (err)
			break;

		this.name = name;
		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.hash = end_name_hash(hash);

		/* remove trailing slashes? */
		if (!c)
			goto last_component;
		while (*++name == ‘/‘);
		if (!*name)
			goto last_with_slashes;

		/*
		 * "." and ".." are special - ".." especially so because it has
		 * to be able to know about the current root directory and
		 * parent relationships.
		 */
		if (this.name[0] == ‘.‘) switch (this.len) {
			default:
				break;
			case 2:
				if (this.name[1] != ‘.‘)
					break;
				follow_dotdot(nd);
				inode = nd->dentry->d_inode;
				/* fallthrough */
			case 1:
				continue;
		}
		/*
		 * See if the low-level filesystem might want
		 * to use its own hash..
		 */
		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
			if (err < 0)
				break;
		}
		/* This does the actual lookups.. */
		dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
		if (!dentry) {
			dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
			err = PTR_ERR(dentry);
			if (IS_ERR(dentry))
				break;
		}
		/* Check mountpoints.. */
		while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
			;

		err = -ENOENT;
		inode = dentry->d_inode;
		if (!inode)
			goto out_dput;
		err = -ENOTDIR;
		if (!inode->i_op)
			goto out_dput;

		if (inode->i_op->follow_link) {
			err = do_follow_link(dentry, nd);
			dput(dentry);
			if (err)
				goto return_err;
			err = -ENOENT;
			inode = nd->dentry->d_inode;
			if (!inode)
				break;
			err = -ENOTDIR;
			if (!inode->i_op)
				break;
		} else {
			dput(nd->dentry);
			nd->dentry = dentry;
		}
		err = -ENOTDIR;
		if (!inode->i_op->lookup)
			break;
		continue;
		/* here ends the main loop */

last_with_slashes:
		lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
		if (lookup_flags & LOOKUP_PARENT)
			goto lookup_parent;
		if (this.name[0] == ‘.‘) switch (this.len) {
			default:
				break;
			case 2:
				if (this.name[1] != ‘.‘)
					break;
				follow_dotdot(nd);
				inode = nd->dentry->d_inode;
				/* fallthrough */
			case 1:
				goto return_base;
		}
		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
			if (err < 0)
				break;
		}
		dentry = cached_lookup(nd->dentry, &this, 0);
		if (!dentry) {
			dentry = real_lookup(nd->dentry, &this, 0);
			err = PTR_ERR(dentry);
			if (IS_ERR(dentry))
				break;
		}
		while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))//在找到/mnt/sdb节点后,会执行到这里,也是不同的所在
			;
		inode = dentry->d_inode;
		if ((lookup_flags & LOOKUP_FOLLOW)
		    && inode && inode->i_op && inode->i_op->follow_link) {
			err = do_follow_link(dentry, nd);
			dput(dentry);
			if (err)
				goto return_err;
			inode = nd->dentry->d_inode;
		} else {
			dput(nd->dentry);
			nd->dentry = dentry;
		}
		err = -ENOENT;
		if (!inode)
			goto no_inode;
		if (lookup_flags & LOOKUP_DIRECTORY) {
			err = -ENOTDIR;
			if (!inode->i_op || !inode->i_op->lookup)
				break;
		}
		goto return_base;
no_inode:
		err = -ENOENT;
		if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
			break;
		goto return_base;
lookup_parent:
		nd->last = this;
		nd->last_type = LAST_NORM;
		if (this.name[0] != ‘.‘)
			goto return_base;
		if (this.len == 1)
			nd->last_type = LAST_DOT;
		else if (this.len == 2 && this.name[1] == ‘.‘)
			nd->last_type = LAST_DOTDOT;
return_base:
		return 0;
out_dput:
		dput(dentry);
		break;
	}
	path_release(nd);
return_err:
	return err;
}

d_mountpoint,查看是否是挂载点。

static __inline__ int d_mountpoint(struct dentry *dentry)
{
	return !list_empty(&dentry->d_vfsmnt);
}

还记得在Linux内核源代码情景分析-文件系统的安装,一文中,最后add_vfsmnt。

list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt);

所以这里非空,返回true,接着执行__follow_down,代码如下:

static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry)
{
	struct list_head *p;
	spin_lock(&dcache_lock);
	p = (*dentry)->d_vfsmnt.next;
	while (p != &(*dentry)->d_vfsmnt) {//参考文件系统的安装的add_vfsmnt
		struct vfsmount *tmp;
		tmp = list_entry(p, struct vfsmount, mnt_clash);
		if (tmp->mnt_parent == *mnt) { //参考文件系统的安装的add_vfsmnt
			*mnt = mntget(tmp);//nd->mnt为被安装设备的vfsmount结构
			spin_unlock(&dcache_lock);
			mntput(tmp->mnt_parent);
			/* tmp holds the mountpoint, so... */
			dput(*dentry);
			*dentry = dget(tmp->mnt_root);//dentry为被安装设备的根节点的dentry结构
			return 1;
		}
		p = p->next;
	}
	spin_unlock(&dcache_lock);
	return 0;
}

还有一个知识点,就是对于中间节点是dot或者dotdot的处理。

if (this.name[0] == ‘.‘) switch (this.len) {
			default:
				break;
			case 2:
				if (this.name[1] != ‘.‘)
					break;
				follow_dotdot(nd);
				inode = nd->dentry->d_inode;
				/* fallthrough */
			case 1:
				continue;

如果是dot,那么就继续下一次循环。如果是dotdot,会调用follow_dotdot,代码如下:

static inline void follow_dotdot(struct nameidata *nd)
{
	while(1) {
		struct vfsmount *parent;
		struct dentry *dentry;
		read_lock(&current->fs->lock);
		if (nd->dentry == current->fs->root &&
		    nd->mnt == current->fs->rootmnt)  {//已到达的nd->dentry就是本进程的根节点,这时不能再往上跑了,所以保持nd->dentry不变
			read_unlock(&current->fs->lock);
			break;
		}
		read_unlock(&current->fs->lock);
		spin_lock(&dcache_lock);
		if (nd->dentry != nd->mnt->mnt_root) {//已到达nd->dentry不等于根节点的dentry结构
			dentry = dget(nd->dentry->d_parent);
			spin_unlock(&dcache_lock);
			dput(nd->dentry);
			nd->dentry = dentry;//dentry结构往上走,mnt结构不变
			break;
		}
		parent=nd->mnt->mnt_parent;//走到这里,说明nd->dentry已经是根节点的dentry结构,只能跑到另一个设备上了,读到父设备的vfsmount结构
		if (parent == nd->mnt) {//如果父设备的vfsmount结构和本设备的vmfmount结构一样,那就不能再往父设备上跑了
			spin_unlock(&dcache_lock);
			break;
		}
		mntget(parent);//如果父设备的vfsmount结构和本设备的vmfmount结构不一样
		dentry=dget(nd->mnt->mnt_mountpoint);//dentry为当前设备的挂载节点的dentry结构
		spin_unlock(&dcache_lock);
		dput(nd->dentry);
		nd->dentry = dentry;
		mntput(nd->mnt);
		nd->mnt = parent;//nd->mnt也被赋值为父设备的vfsmount结构
	}
}
时间: 2024-12-28 09:26:26

Linux内核源代码情景分析-文件系统安装后的访问的相关文章

Linux内核源代码情景分析-文件的写

write对应的系统调用是sys_write,代码如下: asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) { ssize_t ret; struct file * file; ret = -EBADF; file = fget(fd); if (file) { if (file->f_mode & FMODE_WRITE) { struct inode *inode = file-&g

Linux内核源代码情景分析-文件的打开

打开文件的系统调用是open(),在内核中通过sys_open()实现,假设filename是"/usr/local/hello.c",且假设这个文件已经存在,代码如下: asmlinkage long sys_open(const char * filename, int flags, int mode) { char * tmp; int fd, error; #if BITS_PER_LONG != 32 flags |= O_LARGEFILE; #endif tmp = ge

Linux内核源代码情景分析-特殊文件系统/proc

由于proc文件系统并不物理地存在于任何设备上,它的安装过程是特殊的.对proc文件系统不能直接通过mount()来安装,而要先由系统内核在内核初始化时自动地通过一个函数kern_mount()安装一次,然后再由处理系统初始化的进程通过mount()安装,实际上是"重安装". 一.在内核初始化时调用init_proc_fs(),代码如下: static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_

Linux内核源代码情景分析-进程间通信-命名管道

建立命名管道,mknod mypipe p.命名管道存在硬盘上,而管道不是. 通过open打开这个命名管道,在内核中通过sys_open()实现,filename是"***/mypipe ". 相关部分,请参考Linux内核源代码情景分析-文件的打开. sys_open进入filp_open,然后在open_namei中调用一个函数path_walk(),根据文件的路径名在文件系统中找到代表这个文件的inode.在将磁盘上的inode读入内存时,要根据文件的类型(FIFO文件的S_IF

Linux内核源代码情景分析-虚拟文件系统

我们先来看两张图: 第一张是VFS与具体文件系统的关系示意图: 第二张是Linux文件系统的层次结构: 特殊文件:用来实现"管道"的文件,特别是"命名管道"的FIFO文件,还有Unix域的socket,也都属于特殊文件:还有在/proc目录下的一系列文件. 磁盘文件:就是存在硬盘上的文件. 设备文件:sudo mount -t ext2 /dev/sdb1 /mnt/sdb,这里的/dev/sdb1就是设备文件.如果硬盘上的节点raw_inode->i_blo

Linux内核源代码情景分析-mmap后,文件与虚拟区间建立映射

一.文件映射的页面换入 在mmap后,mmap参考Linux内核源代码情景分析-系统调用mmap(),当这个区间的一个页面首次受到访问时,会由于见面无映射而发生缺页异常,相应的异常处理程序do_no_page(). static inline int handle_pte_fault(struct mm_struct *mm, struct vm_area_struct * vma, unsigned long address, int write_access, pte_t * pte) {

Linux内核源代码情景分析-访问权限与文件安全性

在Linux内核源代码情景分析-从路径名到目标节点,一文中path_walk代码中,err = permission(inode, MAY_EXEC)当前进程是否可以访问这个节点,代码如下: int permission(struct inode * inode,int mask) { if (inode->i_op && inode->i_op->permission) { int retval; lock_kernel(); retval = inode->i_

Linux内核源代码情景分析-共享内存

一.库函数shmget()--共享内存区的创建与寻找 asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) { struct shmid_kernel *shp; int err, id = 0; down(&shm_ids.sem); if (key == IPC_PRIVATE) { err = newseg(key, shmflg, size);//分配一个共享内存区供本进程专用,最后返回的是一体化的标示号 } el

Linux内核源代码情景分析-交换分区

在Linux内核源代码情景分析-共享内存中,共享内存,当内存紧张时是换出到交换分区. 在Linux内核源代码情景分析-mmap后,文件与虚拟区间建立映射中,文件映射的页面,当内存紧张时是换出到硬盘上的文件中. 这里的交换分区,就是是swap分区,记得给电脑安装ubuntu时,就有一项是swap分区. 交换分区和文件的区别是: 文件是在一个具体的文件系统之下的,交换分区没有这个必要,它可能是一个裸分区,不需要文件系统. 需要说明一点,并不是所有从物理内存中交换出来的数据都会被放到Swap中(如果这