前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux内核源码分析 - open 续

Linux内核源码分析 - open 续

作者头像
KINGYT
修改2019-06-11 15:03:53
4K0
修改2019-06-11 15:03:53
举报

接上篇,我们继续看下vfs_open方法。

代码语言:javascript
复制
// fs/open.c
int vfs_open(const struct path *path, struct file *file)
{
        file->f_path = *path;
        return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
}

该方法又调用了do_dentry_open方法。

代码语言:javascript
复制
// fs/open.c
static int do_dentry_open(struct file *f,
                          struct inode *inode,
                          int (*open)(struct inode *, struct file *))
{
        ...
        f->f_inode = inode;
        ...
        f->f_op = fops_get(inode->i_fop);
        ...
        if (!open)
                open = f->f_op->open;
        if (open) {
                error = open(inode, f);
                ...
        }
        f->f_mode |= FMODE_OPENED;
        ...
        return 0;
        ...
}

该方法中,设置f->f_op的值为inode->i_fop,由于参数open为null,所以open也被重新赋值为f->f_op->open,即 inode->i_fop->open,之后再调用该open方法,继续执行open逻辑。

那inode->i_fop的值又是在哪里设置的呢?

代码语言:javascript
复制
// fs/inode.c
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
        inode->i_mode = mode;
        if (S_ISCHR(mode)) {
                inode->i_fop = &def_chr_fops;
                inode->i_rdev = rdev;
        } else if (S_ISBLK(mode)) {
                inode->i_fop = &def_blk_fops;
                inode->i_rdev = rdev;
        } else if (S_ISFIFO(mode))
                inode->i_fop = &pipefifo_fops;
        else if (S_ISSOCK(mode))
                ;       /* leave it no_open_fops */
        else
                printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
                                  " inode %s:%lu\n", mode, inode->i_sb->s_id,
                                  inode->i_ino);
}
EXPORT_SYMBOL(init_special_inode);

由上可见,是在init_special_inode方法里设置的。

由于/dev/tty是character device,所以i_fop的值为def_chr_fops。

代码语言:javascript
复制
// fs/char_dev.c
const struct file_operations def_chr_fops = {
        .open = chrdev_open,
        .llseek = noop_llseek,
};

它对应的open方法为chrdev_open。

代码语言:javascript
复制
// fs/char_dev.c
static int chrdev_open(struct inode *inode, struct file *filp)
{
        const struct file_operations *fops;
        struct cdev *p;
        ...
        p = inode->i_cdev;
        if (!p) {
                struct kobject *kobj;
                ...
                kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
                ...
                new = container_of(kobj, struct cdev, kobj);
                ...
                /* Check i_cdev again in case somebody beat us to it while
                   we dropped the lock. */
                p = inode->i_cdev;
                if (!p) {
                        inode->i_cdev = p = new;
                        ...
                } ...
        }
        ...
        fops = fops_get(p->ops);
        ...
        replace_fops(filp, fops);
        if (filp->f_op->open) {
                ret = filp->f_op->open(inode, filp);
                ...
        }

        return 0;
        ...
}

该方法先调用kobj_lookup方法,在cdev_map中找对应的cdev,找到之后把结果赋值给p。之后获取p->ops的值,赋值给fops,再之后替换filp->f_op字段的值为fops,最后检查filp->f_op的值中是否包含open方法,如果有,则调用该方法继续执行open逻辑。

我们先看下/dev/tty对应的cdev是在哪把自己注册到cdev_map里的。

代码语言:javascript
复制
// drivers/tty/tty_io.c
int __init tty_init(void)
{
        ...
        cdev_init(&tty_cdev, &tty_fops);
        if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
                panic("Couldn't register /dev/tty driver\n");
        ...
        return 0;
}

该方法先调用cdev_init,初始化tty_cdev,并将其ops字段设置为tty_fops,然后调用cdev_add、register_chrdev_region方法,注册这个cdev到cdev_map。

由上可知,/dev/tty对应的cdev就是tty_cdev,而cdev->ops就是tty_fops。、

代码语言:javascript
复制
// drivers/tty/tty_io.c
static const struct file_operations tty_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
        .write          = tty_write,
        .poll           = tty_poll,
        .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
        .fasync         = tty_fasync,
        .show_fdinfo    = tty_show_fdinfo,
};

由上可见,cdev->ops->open对应的方法就是tty_open,即/dev/tty的最终open逻辑。

由于此部分逻辑和open系统调用关联不是很大,在此略过。

至此,整个open逻辑就已分析完毕。

完。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档