Linux 设备驱动中的file_operations
Linxu驱动中的设备文件注册的操作方法结构体,也是向用户层提供操作接口的方法体,我的版本为3.1.10
撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/43408625
原型在 内核源码 /include/linux/fs.h中定义:
struct file_operations { struct module *owner; //第一个 file_operations 成员根本不是一个操作; 它是一个指向拥有这个结构的模块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏. loff_t (*llseek) (struct file *, loff_t, int); //llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数. ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); //初始化一个异步读 -- 可能在函数返回前不结束的读操作. 如果这个方法是 NULL, 所有的操作会由 read 代替进行(同步地). ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); //初始化设备上的一个异步写. int (*readdir) (struct file *, void *, filldir_t); //对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用. unsigned int (*poll) (struct file *, struct poll_table_struct *); //poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用作查询对一个或多个文件描述符的读或写是否会阻塞. poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 并且, 可能地, 提供给内核信息用来使调用进程睡眠直到 I/O 变为可能. 如果一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写. long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); // 2.6.35 之后的版本只有这两个ioctl,少了inode参数,功能不变 表示系统调用提供了发出设备特定命令的方法 int (*mmap) (struct file *, struct vm_area_struct *); //mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV. int (*open) (struct inode *, struct file *); //尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知. int (*flush) (struct file *, fl_owner_t id); //flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待)设备的任何未完成的操作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用; SCSI 磁带驱动使用它, 例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求. int (*release) (struct inode *, struct file *); //在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL. int (*fsync) (struct file *, loff_t, loff_t, int datasync); //这个方法是 fsync 系统调用的后端, 用户调用来刷新任何挂着的数据. 如果这个指针是 NULL, 系统调用返回 -EINVAL. int (*aio_fsync) (struct kiocb *, int datasync); //这是 fsync 方法的异步版本. int (*fasync) (int, struct file *, int); //这个操作用来通知设备它的 FASYNC 标志的改变. 异步通知是一个高级的主题, 在第 6 章中描述. 这个成员可以是NULL 如果驱动不支持异步通知. int (*lock) (struct file *, int, struct file_lock *); //lock 方法用来实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几乎从不实现它. ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); //sendpage 是 sendfile 的另一半; 它由内核调用来发送数据, 一次一页, 到对应的文件. 设备驱动实际上不实现 sendpage. unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); //这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存段中. 这个任务通常由内存管理代码进行; 这个方法存在为了使驱动能强制特殊设备可能有的任何的对齐请求. 大部分驱动可以置这个方法为 NULL. int (*check_flags)(int); //这个方法允许模块检查传递给 fnctl(F_SETFL...) 调用的标志. int (*flock) (struct file *, int, struct file_lock *); //文件加锁 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); //用于从管道传输文件数据,目前只用于splice2系统调用; int (*setlease)(struct file *, long, struct file_lock **); // 设置一个打开标志到open的file long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); // 通过设置mode标志FA_ALLOCATE或FA_DEALLOCATE,表示preallocation and deallocation of preallocated blocks respectively,由sys_fallocate调用。 };
拿上几篇博客中提到过的例子 evdev.c中的 event%d 设备文件的file_operations :
static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat, #endif .fasync = evdev_fasync, .flush = evdev_flush, .llseek = no_llseek, };
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。