• 沒有找到結果。

file_operations(文件操作)结构

第 6 章 字符设备驱动程序

6.1 字符设备驱动介绍

6.1.1 字符设备驱动相关的重要结构

6.1.1.1 file_operations(文件操作)结构

由于用户进程是通过设备文件同硬件打交道,所以对设备文件的操作方式不外乎就是一 些系统调用,如 open,read,write,close 等,注意,不是 fopen,fread,fwrite,fclose。但 是如何把系统调用 和驱动程序 关联起来呢? 这时 需要一个非常关键 的数据结构 ,即 file_operations,它用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该数据 结构体在 Linux 2.6.10 内核中具体定义如下:

struct file_operations { struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int datasync);

int (*aio_fsync) (struct kiocb *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

int (*check_flags)(int);

int (*dir_notify)(struct file *filp, unsigned long arg);

int (*flock) (struct file *, int, struct file_lock *);

};

该结构体的每个成员都对应着驱动内核模块用来处理某个被请求事务的函数的地址。下 面对这个定义相对复杂且非常重要的数据结构成员进行解释:

n struct module *owner;

该成员是 file_operations 结构中唯一一个不是声明操作的成员,它是一个指向拥有这个模 块的指针,该成员用来在它的操作还在使用时不允许卸载该模块,通常情况下,都被简单 的初始化为 THIS_MODULE。

n loff_t (*llseek) (struct file *, loff_t, int);

该成员为 file_operations 结构的一个操作,用来改变当前文件的读写位置,并且将新位置 作为返回值。其中 loff_t 为 long long 类型的别名。

n ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

该操作用来从设备中获取数据,如果这个成员为一个空指针,则系统调用 read 将返回一 个-EINVAL(Invalid argument,即无效参数)错误,正常情况下,返回一个非负整数代表 读取的字节数。其中 ssize_t 为 int 或 long 型,和平台相关。__user 用来声明为用户空间。

n ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);

该操作用来初始化一个异步的读操作,即当一个读操作还没有完成时也许这个函数已经返 回。当这个操作为空时,它将由 read(同步)操作代替。

n ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

该操作用来发送数据给设备,当为空时,系统调用 write 将返回-EINVAL 错误。正常情况 下,返回一个非负整数代表成功写的字节数。

n ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);

该操作用来初始化一个异步写操作,当该操作为空时,调用 write 操作。

n int (*readdir) (struct file *, void *, filldir_t);

该操作用于文件系统,用来读取目录。对于设备文件时,该操作为空。

n unsigned int (*poll) (struct file *, struct poll_table_struct *);

该操作用用来查询一个或多个文件描述符的读或写是否会阻塞。Poll 方法返回一个位掩码 来指示是否非阻塞的读或写是可能的,并且提供给内核信息用来使调用进程 sleep 直到 I/O

端口变为可用。如果一个设备驱动的 poll 方法为空,则设备默认为不阻塞的可读和可写。

n int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

该操作用来提供发出设备特定命令的方法。对于一个未定义 ioctl 操作的设备,当系统调 用时将返回-ENOTTY 错误。

n int (*mmap) (struct file *, struct vm_area_struct *);

该操作用来请求将设备内存映射到进程的地址空间。如果这个方法为空,nmap 系统调用

n int (*fsync) (struct file *, struct dentry *, int datasync);

该操作用来刷新任何等待处理的数据,如果这个操作为空,则系统调用 fsync 将返回 -EINVAL。

n int (*aio_fsync) (struct kiocb *, int datasync);

该操作是 fsync 的异步版本。

n int (*fasync) (int, struct file *, int);

该操作用来通知设备 FASYNC 标志的改变。如果该操作为空,则说明该驱动不支持异步 通知。

n int (*lock) (struct file *, int, struct file_lock *);

该操作用来对文件实行加锁,加锁对常规文件是必不可少的特性,但是设备驱动很少有实 现该操作的。

n ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

该操作用来实现多个内存区的单个 read 操作,该操作不必对数据进行额外拷贝。如果该 操作为空,则会调用 read 方法。

n ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

该操作用来实现多个内存区的单个 write 操作,该操作不必对数据进行额外拷贝。如果该 操作为空,则会调用 write 方法。

n ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);

该操作用来实现使用最少的拷贝从一个文件描述符搬移数据到另一个。通常设备驱动使 sendfile 为空。

n ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

该操作用来由内核调用来发送数据,一次一页到对应的文件。设备驱动程序实际上不实现 sendpage 方法。

n unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

该操作用来在进程地址空间找一个合适的位置来映射在底层设备上的内存段中。该方法是 使驱动能强制满足特殊设备的对齐请求。通常情况下,设置该方法为空。

n int (*check_flags)(int);

该操作允许模块检查传递给 fnctl(F_SETFL…)调用的标志。通常情况下,设置该方法为空。

n int (*dir_notify)(struct file *filp, unsigned long arg);

该操作只对文件系统有用,该方法在应用程序使用 fcntl 函数来请求目录改变通知时调用。

设备驱动程序不需要实现 dir_notify 方法。

n int (*flock) (struct file *, int, struct file_lock *);

该操作用来对文件设备加锁,但是基本上没有设备驱动程序实现该操作。

结构体 file_operations 的确包含了很多操作,但在实际设备驱动程序中只会用到其中的 很少一部分,大部分操作将不会被用到。例如在 5.3.4.3 小节中的 Linux 中断实例中的文件 操作定义如下:

static struct file_operations key_fops = {

owner: THIS_MODULE, read: key_read,

open: key_open, release: key_release, };

通过这个实例可以看出,该设备驱动模块只实现了 read、open 和 release 三个操作,这 三个操作所对应的实现函数分别为:key_read、key_open 和 key_release,其他的操作都没有 实现。