走马观花: 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;
}