Linux系统调用open函数分析

走马观花: Linux 系统调用 open 七日游(一)-qh997-ChinaUnix博客

走马观花: Linux 系统调用 open 七日游(二)-qh997-ChinaUnix博客

走马观花: Linux 系统调用 open 七日游(三)-qh997-ChinaUnix博客

走马观花: Linux 系统调用 open 七日游(四)-qh997-ChinaUnix博客

走马观花: Linux 系统调用 open 七日游(六)-qh997-ChinaUnix博客

走马观花: Linux 系统调用 open 七日游(七)-qh997-ChinaUnix博客

Linux 虚拟文件系统 四大对象:超级块、inode、dentry、file - 知乎

补充:

1、补充path、vfsmount、mount和dentry等结构体定义:

struct path {
	struct vfsmount *mnt;
	struct dentry *dentry;
} __randomize_layout;
struct vfsmount {
	struct dentry *mnt_root;	/* root of the mounted tree */
	struct super_block *mnt_sb;	/* pointer to superblock */
	int mnt_flags;
	void *data;
} __randomize_layout;
struct mount {
	struct hlist_node mnt_hash;
	struct mount *mnt_parent;
	struct dentry *mnt_mountpoint;
	struct vfsmount mnt;
    ......
}
struct dentry {
	unsigned int d_flags;		/* protected by d_lock */
	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 */
	const struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	void *d_fsdata;			/* fs-specific data */
    .......
} __randomize_layout;

1.1:path结构体中的mnt指针指向mount结构体中的mnt成员变量。

1.2:vsfmount是mount的成员,通过vfsmount可以找到对应的mount。

2、判断目录项是否是挂载点:

static inline bool d_mountpoint(const struct dentry *dentry)
{
	return dentry->d_flags & DCACHE_MOUNTED;
}

3、目录项: 

 4、一个文件路径可以分成多个目录项,经路径分析后的最终目录项都包含一个与之挂钩的具体的文件系统。路径分析(路径行走)分为正向分析和反向分析,正常情况下是正向的,但是遇到“..”时就需要反向。不同的文件系统的分水岭在挂载点,所以要特别关注挂载点处的目录项的处理。另外当前目录项是根据目录名和父目录项搜索确定的。

正向路径行走的挂载点处理:

 __lookup_mnt函数获取挂载点处的下一个文件系统:

反向路径行走的挂载点处理:

如下图注意区分mountpoint和mnt_root,mountpoint是挂载时上一个文件系统的目录项,mnt_root是下一个文件系统的根目录项。

 5、代码分析,以文件路径"/ww/1.txt"为例:

do_filp_open
    path_openat

path_openat函数:

static struct file *path_openat(struct nameidata *nd,const struct open_flags *op, unsigned                 
                                   flags)
{
	const char *s;
	struct file *file;
    int error;

	file = get_empty_filp();
	s = path_init(nd, flags);  // s="/ww/1.txt"
	while (!(error = link_path_walk(s, nd))&&(error = do_last(nd, file, op, &opened)) > 0)     
    {}
}

 path_init:路径初始化,分为绝对路径和相对路径

static const char *path_init(struct nameidata *nd, unsigned flags)
{
	const char *s = nd->name->name; //s="/ww/1.txt"

	nd->last_type = LAST_ROOT;
	nd->depth = 0;    //用于符号链接的嵌套,这里不讨论

	nd->root.mnt = NULL;
	nd->path.mnt = NULL;
	nd->path.dentry = NULL;

	if (*s == '/') {
		set_root(nd);
        if (likely(!nd_jump_root(nd)))
		    return s;
	}else if (nd->dfd == AT_FDCWD) {    //相对路径:相对当前进程所在路径
		if (flags & LOOKUP_RCU) {
			struct fs_struct *fs = current->fs;
			nd->path = fs->pwd;
			nd->inode = nd->path.dentry->d_inode;
		}
        return s;
    } 
}

 set_root:设置根目录

static void set_root(struct nameidata *nd)
{
	struct fs_struct *fs = current->fs;

	if (nd->flags & LOOKUP_RCU) {
		nd->root = fs->root;
	} 
}

 nd_jump_root:设置nd当前目录为"/"目录,并开始准备往前走过此"/"目录。

static int nd_jump_root(struct nameidata *nd)
{
	if (nd->flags & LOOKUP_RCU) {
		struct dentry *d;
		nd->path = nd->root;
		d = nd->path.dentry;    // nd->path.dentry对应"/"目录
		nd->inode = d->d_inode;
	} 
	nd->flags |= LOOKUP_JUMPED;
	return 0;
}

 link_path_walk:逐个走过文件路径"/ww/1.txt"中除了"/"和"1.txt"之外的所有目录项,即"ww"。

static int link_path_walk(const char *name, struct nameidata *nd)
{
	int err;

	while (*name=='/')
		name++;
	if (!*name)
		return 0;

	for(;;) {
		u64 hash_len;    //hash值和目录名的长度的拼接
		int type;

        //第1遍:nd->path.dentry为"/"目录,name="ww/1.txt",
        //       hash_len: len为字符串"ww"的长度,即2
        //第2遍:nd->path.dentry为"ww"目录,name="1.txt",
		hash_len = hash_name(nd->path.dentry, name);

		type = LAST_NORM;
		if (name[0] == '.') switch (hashlen_len(hash_len)) { 
			case 2:
				if (name[1] == '.') {
					type = LAST_DOTDOT;
					nd->flags |= LOOKUP_JUMPED;
				}
				break;
			case 1:
				type = LAST_DOT;
		}

		if (likely(type == LAST_NORM)) {
			struct dentry *parent = nd->path.dentry;
			nd->flags &= ~LOOKUP_JUMPED;
			if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
				struct qstr this = { { .hash_len = hash_len }, .name = name };
				err = parent->d_op->d_hash(parent, &this);
				hash_len = this.hash_len;
				name = this.name;
			}
		}
        
        //第2遍:nd->last.name="1.txt"
		nd->last.hash_len = hash_len;
		nd->last.name = name;
		nd->last_type = type;

        //第1遍:name="/1.txt"
        //第2遍:name=NULL,nd->last.name为最后一个目录项时函数结束
		name += hashlen_len(hash_len);
		if (!*name)
			goto OK;

		do {
			name++;
		} while (unlikely(*name == '/'));
        
        //第1遍:name="1.txt"
		if (unlikely(!*name)) {
OK:
			if (!nd->depth) // nd->depth为0,没有符号链接
				return 0;
		} else {
			/* not the last component */
			err = walk_component(nd, WALK_FOLLOW | WALK_MORE);
		}
		
		
	}
}
#define hashlen_hash(hashlen) ((u32)(hashlen))
#define hashlen_len(hashlen)  ((u32)((hashlen) >> 32))
#define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash))
static int walk_component(struct nameidata *nd, int flags)
{
	struct path path;
	struct inode *inode;

	int err;

	if (unlikely(nd->last_type != LAST_NORM)) {
		err = handle_dots(nd, nd->last_type);
		return err;
	}
	err = lookup_fast(nd, &path, &inode, &seq);
	
	return step_into(nd, &path, flags, inode, seq);
}
static int lookup_fast(struct nameidata *nd,struct path *path, struct inode **inode,
		               unsigned *seqp)
{
	struct vfsmount *mnt = nd->path.mnt;
	struct dentry *dentry, *parent = nd->path.dentry;
	int status = 1;
	int err;

	if (nd->flags & LOOKUP_RCU) {
		unsigned seq;
		dentry = __d_lookup_rcu(parent, &nd->last, &seq);
	
		path->mnt = mnt;
		path->dentry = dentry;
		if (likely(__follow_mount_rcu(nd, path, inode, seqp)))
			return 1;
    }
}
struct dentry *__d_lookup_rcu(const struct dentry *parent,
				const struct qstr *name,
				unsigned *seqp)
{
	u64 hashlen = name->hash_len;
	const unsigned char *str = name->name;
	struct hlist_bl_head *b = d_hash(hashlen_hash(hashlen));
	struct hlist_bl_node *node;
	struct dentry *dentry;

    //哈希链表查找,通过比较父目录项,hashlen值和目录项名确定要查找的目录项。
	hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {

		if (dentry->d_parent != parent)
			continue;

		if (dentry->d_name.hash_len != hashlen)
			continue;
		if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0)
				continue;
		return dentry;
	}
}
static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
			       struct inode **inode, unsigned *seqp)
{
		struct mount *mounted;

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

        //正向行走,是挂载点的话,找下一个文件系统的mnt_root
		mounted = __lookup_mnt(path->mnt, path->dentry);
		if (!mounted)
			break;
		path->mnt = &mounted->mnt;
		path->dentry = mounted->mnt.mnt_root;
		nd->flags |= LOOKUP_JUMPED;
		*inode = path->dentry->d_inode;

}

 step_into:进入,nd->path真正往前走一步

static inline int step_into(struct nameidata *nd, struct path *path,
			    int flags, struct inode *inode, unsigned seq)
{
    //path->dentry不是符号链接,nd->path真正往前走一步,nd->path->dentry为path->dentry
	if (likely(!d_is_symlink(path->dentry)) ||
	   !(flags & WALK_FOLLOW || nd->flags & LOOKUP_FOLLOW)) {

		path_to_nameidata(path, nd);
		nd->inode = inode;
		return 0;
	}

}
static inline void path_to_nameidata(const struct path *path,
					struct nameidata *nd)
{
	nd->path.mnt = path->mnt;
	nd->path.dentry = path->dentry;
}
static inline u64 hash_name(const void *salt, const char *name)
{
	unsigned long hash = init_name_hash(salt);
	unsigned long len = 0, c;

	c = (unsigned char)*name;
	do {
		len++;
		hash = partial_name_hash(c, hash);
		c = (unsigned char)name[len];
	} while (c && c != '/');
	return hashlen_create(end_name_hash(hash), len);
}

do_last函数对最后一个目录项进行处理。

static int do_last(struct nameidata *nd,
		   struct file *file, const struct open_flags *op,
		   int *opened)
{
	struct dentry *dir = nd->path.dentry;
	int open_flag = op->open_flag;
	struct inode *inode;
	struct path path;
	int error;

	nd->flags &= ~LOOKUP_PARENT;
	nd->flags |= op->intent;


	if (!(open_flag & O_CREAT)) {

		//通过目录项名等查找最后一个目录项
		error = lookup_fast(nd, &path, &inode, &seq);
		if (likely(error > 0))
			goto finish_lookup;
	} 

finish_lookup:
	step_into(nd, &path, 0, inode, seq);

finish_open:
	vfs_open(&nd->path, file, current_cred());

}
int vfs_open(const struct path *path, struct file *file,
	     const struct cred *cred)
{
	struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags, 0);

	file->f_path = *path;
	return do_dentry_open(file, d_backing_inode(dentry), NULL, cred);
}

static int do_dentry_open(struct file *f,
			  struct inode *inode,
			  int (*open)(struct inode *, struct file *),
			  const struct cred *cred)
{
	static const struct file_operations empty_fops = {};
	int error;

	f->f_inode = inode;

	f->f_op = fops_get(inode->i_fop);

	if (!open)
		open = f->f_op->open;
	if (open) {
		open(inode, f);
	}

	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值