接上篇,我们继续看下vfs_open方法。
// 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方法。
// 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的值又是在哪里设置的呢?
// 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。
// fs/char_dev.c
const struct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
它对应的open方法为chrdev_open。
// 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里的。
// 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。、
// 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逻辑就已分析完毕。
完。
本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!