作为例子,这里假定前面对应于/dev/demodev设备节点的驱动程序在自己的代码里实现了如下的struct file_operations对象fops:
static struct file_operations fops = {
.open = demoopen,
.read = demoread,
.write = demowrite,
.ioctl = demoioctl,
};
用户空间open函数的原型为:
int open(const char *filename, int flags, mode_t mode);
这个函数如果成功,将返回一个文件描述符,否则返回-1。函数的第一个参数filename表示要打开的文件名,第二个参数flags用于指定文件的打开或者创建模式,本书在后续"字符设备的高级操作"一章中会讨论其中一些常见取值对驱动程序的影响,最后一个参数mode只在创建一个新文件时才使用,用于指定新建文件的访问权限,比如可读、可写及可执行等权限。
位于内核空间的驱动程序中open函数的原型为:
<include/linux/fs.h>
struct file_operations {
…
int (*open) (struct inode *, struct file *);
…
};
两者相比差异很大。接下来我们将描述从用户态的open是如何一步一步调用到驱动程序提供的open函数(在我们的例子中,它的具体实现是demoopen)的。如同设备文件节点的生成一样,透彻了解这里的每一个步骤也需要掌握全面的Linux下文件系统的技术细节。从设备驱动程序员的角度,我们依然将重点放在两者如何建立联系的关键点上。
用户程序调用open函数返回的文件描述符,本文用fd表示,这是个int型的变量,会被用户程序后续的read、write和ioctl等函数所使用。同时可以看到,在驱动程序中的demodev_read、demodev_write和demodev_ioctl等函数其第一个参数都是struct file *filp。显然内核需要在打开设备文件时为fd与filp建立某种联系,其次是为filp与驱动程序中的fops建立关联。
用户空间程序调用open函数,将发起一个系统调用,通过sys_open函数进入内核空间,其中一系列关键的函数调用关系如图2-8所示:

do_sys_open函数首先通过get_unused_fd_flags为本次的open操作分配一个未使用过的文件描述符fd :
<fs/open.c>
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
…
fd = get_unused_fd_flags(flags);
…
}
get_unused_fd_flags实际上是封装了alloc_fd的一个宏,真正分配fd的操作发生在alloc_fd函数中,后者会涉及大量文件系统方面的细节,这不是本书的主题。读者这里只需知道alloc_fd将会为本次的open操作分配一个新的fd。
do_sys_open随后调用do_filp_open函数,后者会首先查找"/dev/demodev"设备文件所对应的inode。在Linux文件系统中,每个文件都有一个inode与之对应。从文件名查找对应的inode这一过程,同样会涉及大量文件系统方面的细节。
do_filp_open在成功查找到"/dev/demodev"设备文件对应的inode之后,接着会调用函数get_empty_filp,后者会为每个打开的文件分配一个新的struct file类型的内存空间(本书将把指向该结构体对象的内存指针简写为filp):
<fs/namei.c>
struct file *do_filp_open(int dfd, const char *pathname,
const struct open_flags *op, int flags)
{
struct nameidata nd;
struct file *filp;
filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
…
return filp;
}
内核用struct file对象来描述进程打开的每一个文件的视图,即使是打开同一文件,内核也会为之生成一个新的struct file对象,用来表示当前操作的文件的相关信息,其定义为:
<include/linux/fs.h>
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};