在linux中inode结构用于表示文件,而file结构则表示打开的文件的描述,由于对于单个文件而言可能会有许多个表示打开的文件的描述符,于是就可能会的对应有多个file结构,可是都指向单个inode结构。php
在系统内部,I/O设备的存取操做经过特定的的入口来进行,而这组特定的入口由驱动程序来提供的。一般这组设备驱动的接口是由结构体file_operations向系统说明的node
第一个 file_operations 成员根本不是一个操做; 它是一个指向拥有这个结构的模块的指针. 这个成员用来在它的操做还在被使用时阻止模块被卸载. 几乎全部时间中, 它被简单初始化为 THIS_MODULE, 一个在 中定义的宏.这个宏比较复杂,在进行简单学习操做的时候,通常初始化为THIS_MODULE。 loff_t (*llseek) (struct file * filp , loff_t p, int orig); (指针参数filp为进行读取信息的目标文件结构体指针;参数 p 为文件定位的目标偏移量;参数orig为对文件定位 的起始地址,这个值能够为文件开头(SEEK_SET,0,当前位置(SEEK_CUR,1),文件末尾(SEEK_END,2)) llseek 方法用做改变文件中的当前读/写位置, 而且新位置做为(正的)返回值. loff_t 参数是一个"long offset", 而且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 若是这个函数指针是 NULL, seek 调用会以潜在地没法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ssize_t (*read) (struct file * filp, char __user * buffer, size_t size , loff_t * p); (指针参数 filp 为进行读取信息的目标文件,指针参数buffer 为对应放置信息的缓冲区(即用户空间内存地址), 参数size为要读取的信息长度,参数 p 为读的位置相对于文件开头的偏移,在读取信息后,这个指针通常都会移动,移动的值为要读取信息的长度值) 这个函数用来从设备中获取数据. 在这个位置的一个空指针致使 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值表明了成功读取的字节数( 返回值是一个 "signed size" 类型, 经常是目标平台本地的整数类型). ssize_t (*aio_read)(struct kiocb * , char __user * buffer, size_t size , loff_t p); 能够看出,这个函数的第1、三个参数和本结构体中的read()函数的第1、三个参数是不一样 的, 异步读写的第三个参数直接传递值,而同步读写的第三个参数传递的是指针,由于AIO历来不须要改变文件的位置。 异步读写的第一个参数为指向kiocb结构体的指针,而同步读写的第一参数为指向file结构体的指针,每个I/O请求都对应一个kiocb结构体); 初始化一个异步读 -- 可能在函数返回前不结束的读操做.若是这个方法是 NULL, 全部的操做会由 read 代替进行(同步地). (有关linux异步I/O,能够参考有关的资料,《linux设备驱动开发详解》中给出了详细的解答) ssize_t (*write) (struct file * filp, const char __user * buffer, size_t count, loff_t * ppos); (参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度, ppos为当前的偏移位置,这个值一般是用来判断写文件是否越界) 发送数据给设备. 若是 NULL, -EINVAL 返回给调用 write 系统调用的程序. 若是非负, 返回值表明成功写的字节数. (注:这个操做和上面的对文件进行读的操做均为阻塞操做) ssize_t (*aio_write)(struct kiocb *, const char __user * buffer, size_t count, loff_t * ppos); 初始化设备上的一个异步写.参数类型同aio_read()函数; int (*readdir) (struct file * filp, void *, filldir_t); 对于设备文件这个成员应当为 NULL; 它用来读取目录, 而且仅对文件系统有用. unsigned int (*poll) (struct file *, struct poll_table_struct *); (这是一个设备驱动中的轮询函数,第一个参数为file结构指针,第二个为轮询表指针) 这个函数返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位“或”结果。 每一个宏都代表设备的一种状态,如:POLLIN(定义为0x0001)意味着设备能够无阻塞的读,POLLOUT(定义为0x0004)意味着设备能够无阻塞的写。 (poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用做查询对一个或多个文件描述符的读或写是否会阻塞. poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 而且, 可能地, 提供给内核信息用来使调用进程睡眠直到 I/O 变为可能. 若是一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写. (这里一般将设备看做一个文件进行相关的操做,而轮询操做的取值直接关系到设备的响应状况,能够是阻塞操做结果,同时也能够是非阻塞操做结果) int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); (inode 和 filp 指针是对应应用程序传递的文件描述符 fd 的值, 和传递给 open 方法的相同参数. cmd 参数从用户那里不改变地传下来, 而且可选的参数 arg 参数以一个 unsigned long 的形式传递, 无论它是否由用户给定为一个整数或一个指针. 若是调用程序不传递第 3 个参数, 被驱动操做收到的 arg 值是无定义的. 由于类型检查在这个额外参数上被关闭, 编译器不能警告你若是一个无效的参数被传递给 ioctl, 而且任何关联的错误将难以查找.) ioctl 系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外, 几个 ioctl 命令被内核识别而没必要引用 fops 表. 若是设备不提供 ioctl 方法, 对于任何未事先定义的请求(-ENOTTY, "设备无这样的 ioctl"), 系统调用返回一个错误. int (*mmap) (struct file *, struct vm_area_struct *); mmap 用来请求将设备内存映射到进程的地址空间. 若是这个方法是 NULL, mmap 系统调用返回 -ENODEV. (若是想对这个函数有个完全的了解,那么请看有关“进程地址空间”介绍的书籍) int (*open) (struct inode * inode , struct file * filp ) ; (inode 为文件节点,这个节点只有一个,不管用户打开多少个文件,都只是对应着一个inode结构; 可是filp就不一样,只要打开一个文件,就对应着一个file结构体,file结构体一般用来追踪文件在运行时的状态信息) 尽管这经常是对设备文件进行的第一个操做, 不要求驱动声明一个对应的方法. 若是这个项是 NULL, 设备打开一直成功, 可是你的驱动不会获得通知. 与open()函数对应的是release()函数。 int (*flush) (struct file *); flush 操做在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(而且等待)设备的任何未完成的操做. 这个必须不要和用户查询请求的 fsync 操做混淆了. 当前, flush 在不多驱动中使用; SCSI 磁带驱动使用它, 例如, 为确保全部写的数据在设备关闭前写到磁带上. 若是 flush 为 NULL, 内核简单地忽略用户应用程序的请求. int (*release) (struct inode *, struct file *); release ()函数当最后一个打开设备的用户进程执行close()系统调用的时候,内核将调用驱动程序release()函数: void release(struct inode inode,struct file *file),release函数的主要任务是清理未结束的输入输出操做,释放资源,用户自定义排他标志的复位等。 在文件结构被释放时引用这个操做. 如同 open, release 能够为 NULL. int(*synch)(struct file *,struct dentry *,int datasync); 刷新待处理的数据,容许进程把全部的脏缓冲区刷新到磁盘。 int (*aio_fsync)(struct kiocb *, int); 这是 fsync 方法的异步版本.所谓的fsync方法是一个系统调用函数。系统调用fsync 把文件所指定的文件的全部脏缓冲区写到磁盘中(若是须要,还包括存有索引节点的缓冲区)。 相应的服务例程得到文件对象的地址,并随后调用fsync方法。一般这个方法以调用函数__writeback_single_inode()结束, 这个函数把与被选中的索引节点相关的脏页和索引节点自己都写回磁盘。 int (*fasync) (int, struct file *, int); 这个函数是系统支持异步通知的设备驱动,下面是这个函数的模板: static int ***_fasync(int fd,struct file *filp,int mode) { struct ***_dev * dev=filp->private_data; return fasync_helper(fd,filp,mode,&dev->async_queue);//第四个参数为 fasync_struct结构体指针的指针。 //这个函数是用来处理FASYNC标志的函数。(FASYNC:表示兼容BSD的fcntl同步操做)当这个标志改变时,驱动程序中的fasync()函数将获得执行。 (注:感受这个‘标志'词用的并不恰当)}此操做用来通知设备它的 FASYNC 标志的改变. 异步通知是一个高级的主题, 在第 6 章中描述.这个成员能够是NULL 若是驱动不支持异步通知.int (*lock) (struct file *, int, struct file_lock *);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 *);这些方法实现发散/汇聚读和写操做. 应用程序偶尔须要作一个包含多个内存区的单个读或写操做;这些系统调用容许它们这样作而没必要对数据进行额外拷贝. 若是这些函数指针为 NULL, read 和 write 方法被调用( 可能多于一次 ).ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);这个方法实现 sendfile 系统调用的读, 使用最少的拷贝从一个文件描述符搬移数据到另外一个.例如, 它被一个须要发送文件内容到一个网络链接的 web 服务器使用. 设备驱动经常使 sendfile 为 NULL.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.[10]int (*check_flags)(int)这个方法容许模块检查传递给 fnctl(F_SETFL...) 调用的标志.int (*dir_notify)(struct file *, unsigned long);这个方法在应用程序使用 fcntl 来请求目录改变通知时调用. 只对文件系统有用; 驱动不须要实现 dir_notify.通常状况下,进行设备驱动程序的设计只是比较注重下面的几个方法:struct file_operations ***_ops={.owner = THIS_MODULE,.llseek = ***_llseek,.read = ***_read,.write = ***_write,.ioctl = ***_ioctl,.open = ***_open,.release = ***_release, }; |