文件系统之open

文件系统之open

在文件读写之前,我们必须先打开文件。从应用程序的角度来看,这是通过标准库的open函数完成的,该函数返回一个文件描述符。内核中是由系统调用do_sys_open()函数完成。

//系统调用
SYSCALL_DEFINE3(open...)
//具体实现
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{               
        struct open_flags op;
        int fd = build_open_flags(flags, mode, &op);
        struct filename *tmp;

        if (fd)
                return fd;

        tmp = getname(filename);
        if (IS_ERR(tmp)) 
                return PTR_ERR(tmp);
        /*在内核中,每个打开的文件由一个文件描述符表示 
        该描述符在特定于进程的数组中充当位置索引(数组是 
        task_struct->files->fd_arry),该数组的元素包含了file结构,其中 
        包括每个打开文件的所有必要信息。因此,调用下面 
        函数查找一个未使用的文件描述符,返回的是上面 
        说的数组的下标*/ 
        fd = get_unused_fd_flags(flags);
        if (fd >= 0) {
                /* 此函数主要功能为打开文件file获得file结构*/
                struct file *f = do_filp_open(dfd, tmp, &op);
                if (IS_ERR(f)) {
                        put_unused_fd(fd);
                        fd = PTR_ERR(f);
                } else {
                        fsnotify_open(f);
                        /*将fd与对应的file结构关联起来*/
                        fd_install(fd, f);
                }
        }
        putname(tmp);
        return fd;
}
//打开文件具体实现
struct file *do_filp_open(int dfd, struct filename *pathname,
                const struct open_flags *op)
{       
        struct nameidata nd;
        int flags = op->lookup_flags;
        struct file *filp;

        /*初始化nameidata结构,该结构是open过程的重要辅助结构,下面单独对其说明*/
        set_nameidata(&nd, dfd, pathname); 
        //查找打开文件的主要函数
        filp = path_openat(&nd, op, flags | LOOKUP_RCU);
        if (unlikely(filp == ERR_PTR(-ECHILD)))
                filp = path_openat(&nd, op, flags);
        if (unlikely(filp == ERR_PTR(-ESTALE)))
                filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
        restore_nameidata();
        return filp;
}

//nameidata结构说明:nameidata在打开每一层目录项时是变化的,主要用于记录当前文件系统current->vfsmount和current->dentry.
struct nameidata {
        struct path     path; //该字段用于保存当前目录项。该字段是path结构,该结构将目录项和该目录项所关联的vfsmount结构进行封装。
        struct qstr     last; //当前目录项的名称
        struct path     root; //当前文件系统的根目录
        struct inode    *inode; /* path.dentry.d_inode */
        unsigned int    flags;
        ...
};
static struct file *path_openat(struct nameidata *nd,
                        const struct open_flags *op, unsigned flags)
{
        const char *s;
        struct file *file;
        int opened = 0;
        int error;

        file = get_empty_filp(); //获得一个新的file结构
        ...
        /*对接下来的路径遍历做一些准备工作,主要用于判断路径遍历的起始位置,即通过根目录/,或当前路径(pwd),或指定路径(openat系统调用可以指定)*/
        s = path_init(nd, flags);
        /*link_path_walk会遍历路径中的每个dentry项,此函数之后,nameidata保存了最后一个目录项的信息,但是内核并没有确定最后一个目录项是否真的存在,这些工作将在do_last()中进行。*/
        while (!(error = link_path_walk(s, nd)) &&
                (error = do_last(nd, file, op, &opened)) > 0) {
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
                s = trailing_symlink(nd);
                if (IS_ERR(s)) {
                        error = PTR_ERR(s);
                        break;
                }
        }
}
static int link_path_walk(const char *name, struct nameidata *nd)
{
    for(;;) {
        err = walk_component(nd, WALK_GET); //遍历路径中的每一项dentry
    }
}

static int walk_component(struct nameidata *nd, int flags)
{
        struct path path;
        struct inode *inode;
        unsigned seq;
        int err;
        /*
         * "." and ".." are special - ".." especially so because it has
         * to be able to know about the current root directory and
         * parent relationships.
         */
        if (unlikely(nd->last_type != LAST_NORM)) {
                err = handle_dots(nd, nd->last_type); //处理.和..特殊dentry项
                if (flags & WALK_PUT)
                        put_link(nd);
                return err;
        }
        err = lookup_fast(nd, &path, &inode, &seq); //搜索dentry cache
        if (unlikely(err <= 0)) {
                if (err < 0)
                        return err;
                path.dentry = lookup_slow(&nd->last, nd->path.dentry,
                                          nd->flags); /*读取磁盘上的内容到内存中的dentry cache进行进一步搜索*/
                if (IS_ERR(path.dentry))
                        return PTR_ERR(path.dentry);
                if (unlikely(d_is_negative(path.dentry))) {
                        dput(path.dentry);
                        return -ENOENT;
                }
                path.mnt = nd->path.mnt;
                err = follow_managed(&path, nd);
                if (unlikely(err < 0))
                        return err;

                seq = 0;        /* we are already out of RCU mode */
                inode = d_backing_inode(path.dentry);
        }
}
static int do_last(struct nameidata *nd,struct file *file, const struct open_flags *op,int *opened)
    //调用vfs_open打开目标文件
    vfs_open(&nd->path, file, current_cred()); //打开目标文件
        //调用do_dentry_open
        do_dentry_open(file, inode, NULL, cred);
            //将目标文件inode对应的具体文件操作函数赋给file结构
            f->f_op = fops_get(inode->i_fop);
            //调用具体文件系统的open函数打开文件,后续就可以进行文件读写操作了
            error = open(inode, f);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值