这个结构中与设备驱动程序关系最密切的是f_op、f_flags、f_count和private_data成员。f_op指针的类型是struct file_operations,恰好我们的字符设备驱动程序中也需要实现一个该类型的对象,马上我们将看到这两者之间是如何建立联系的。f_flags用于记录当前文件被open时所指定的打开模式,这个成员将会影响后续的read/write等函数的行为模式。成员f_count用于对struct file对象的使用计数,当close一个文件时,只有struct file对象中f_count成员为0才真正执行关闭操作。private_data常被用来记录设备驱动程序自身定义的数据,因为filp指针会在驱动程序实现的file_operations对象其他成员函数之间传递,所以可以通过filp中的private_data成员在某一个特定文件视图的基础上共享数据。
进程为文件操作维护一个文件描述符表(current->files->fdt),正如在本节开始部分看到的那样,对设备文件的打开,最终会得到一个文件描述符fd,然后用该描述符fd作为进程维护的文件描述符表(指向struct file *类型数组)的索引值,将之前新分配的struct file空间地址赋值给它:
current->files->fdt->pfd[fd] = filp;
这样,用户空间程序在后续的read、write、ioctl等函数调用中利用fd就可以找到对应的filp,如图2-9所示:

在do_sys_open的后半部分,会调用__dentry_open函数将"/dev/demodev"对应节点的inode中的i_fop赋值给filp->f_op,然后调用i_fop中的open函数:
<fs/open.c>
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
struct inode *inode;
…
f->f_op = fops_get(inode->i_fop);
…
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
…
}
…
}
__dentry_open函数当初在nameidata_to_filp中被调用时,第四个实参是NULL,所以在__dentry_open中,open = f->f_op->open。在上节设备文件节点的生成中,我们知道inode->i_fop = &def_chr_fops,这样filp->f_op = &def_chr_fops。接下来会利用filp中的这个新的f_op作调用:filp->f_op->open(inode, filp),于是chrdev_open函数将被调用到。该函数非常重要,为了突出其主线,下面先将它改写成以下简单几行:
<fs/char_dev.c>
static int chrdev_open(struct inode *inode, struct file *filp)
{
int ret = 0, idx;
struct kobject *kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
struct cdev *new = container_of(kobj, struct cdev, kobj);
inode->i_cdev = new;
list_add(&inode->i_devices, &new->list);
filp->f_op = new->ops;
if (filp->f_op->open) {
ret = filp->f_op->open(inode,filp);
}
return ret;
}
函数首先通过kobj_lookup在cdev_map中用inode->i_rdev来查找设备号所对应的设备new,这里展示了设备号的作用。成功查找到设备后,通过filp->f_op = new->ops这行代码将设备对象new中的ops指针(前面曾讨论过,驱动程序通过调用cdev_init将其实现的file_operations对象的指针赋值给设备对象cdev的ops成员)赋值给filp对象中的f_op成员,此处展示了如何将驱动程序中实现的struct file_operations与filp关联起来,从此图2-9中的filp->f_op将指向驱动程序中实现的struct file_operations对象。
接下来函数会检查驱动程序中是否实现了open函数(if (filp->f_op->open)),如果实现了,就调用设备驱动程序中实现的open函数。打开一个字符设备节点的大体流程如图2-10所示:
