转自:qian-xu-fenghtml
file_operation就是把系统调用和驱动程序关联起来的关键数据结构。这个结构的每个成员都对应着一个系统调用。读取file_operation中相应的函数指针,接着把控制权转交给函数,从而完成了Linux设备驱动程序的工做。node
在系统内部,I/O设备的存取操做经过特定的入口点来进行,而这组特定的入口点偏偏是由设备驱动程序提供的。一般这组设备驱动程序接口是由结构file_operations结构体向系统说明的,它定义在include/linux/fs.h中。linux
传统上,一个file_operation结构或者其一个指针称为fops(或者它的一些变体).结构中的每一个成员必须指向驱动中的函数,这些函数实现一个特别的操做,或者对于不支持的操做留置为NULL.当指定为NULL指针时内核的确切的行为是每一个函数不一样的。web
在你通读file_operations方法的列表时,你会注意到很多参数包含字串__user.这种注解是一种文档形式,注意,一个指针是一个不能被直接解引用的用户空间地址.对于正常的编译, __user没有效果,可是它可被外部检查软件使用来找出对用户空间地址的错误使用。算法
---------------------------------------------------------------------后端
注册设备编号仅仅是驱动代码必须进行的诸多任务中的第一个。首先须要涉及一个别的,大部分的基础性的驱动操做包括3个重要的内核数据结构,称为file_operations,file,和inode。须要对这些结构的基本了解才可以作大量感兴趣的事情。服务器
struct file_operations是一个字符设备把驱动的操做和设备号联系在一块儿的纽带,是一系列指针的集合,每一个被打开的文件都对应于一系列的操做,这就是file_operations,用来执行一系列的系统调用。网络
struct file表明一个打开的文件,在执行file_operation中的open操做时被建立,这里须要注意的是与用户空间inode指针的区别,一个在内核,而file指针在用户空间,由c库来定义。
struct inode被内核用来表明一个文件,注意和struct file的区别,struct inode一个是表明文件,struct file一个是表明打开的文件
struct inode包括很重要的二个成员:
dev_t i_rdev 设备文件的设备号
struct cdev *i_cdev表明字符设备的数据结构
struct inode结构是用来在内核内部表示文件的.同一个文件能够被打开好屡次,因此能够对应不少struct file,可是只对应一个struct inode.数据结构
struct file_operations {app
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);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
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 *);
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);
};
File_operations的数据结构以下:
struct module *owner
第一个file_operations成员根本不是一个操做;它是一个指向拥有这个结构的模块的指针.这个成员用来在它的操做还在被使用时阻止模块被卸载.几乎全部时间中,它被简单初始化为THIS_MODULE,一个在中定义的宏.
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 (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);
初始化一个异步读--可能在函数返回前不结束的读操做.若是这个方法是NULL,全部的操做会由read代替进行(同步地).
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
发送数据给设备.若是NULL, -EINVAL返回给调用write系统调用的程序.若是非负,返回值表明成功写的字节数.
ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, 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,设备假定为不阻塞地可读可写.
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
ioctl系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道,这不是读也不是写).另外,几个ioctl命令被内核识别而没必要引用fops表.若是设备不提供ioctl方法,对于任何未事先定义的请求(-ENOTTY, "设备无这样的ioctl"),系统调用返回一个错误.
int (*mmap) (struct file *, struct vm_area_struct *);
mmap用来请求将设备内存映射到进程的地址空间.若是这个方法是NULL, mmap系统调用返回-ENODEV.
int (*open) (struct inode *, struct file *);
尽管这经常是对设备文件进行的第一个操做,不要求驱动声明一个对应的方法.若是这个项是NULL,设备打开一直成功,可是你的驱动不会获得通知.
int (*flush) (struct file *);
flush操做在进程关闭它的设备文件描述符的拷贝时调用;它应当执行(而且等待)设备的任何未完成的操做.这个必须不要和用户查询请求的fsync操做混淆了.当前, flush在不多驱动中使用; SCSI磁带驱动使用它,例如,为确保全部写的数据在设备关闭前写到磁带上.若是flush为NULL,内核简单地忽略用户应用程序的请求.
int (*release) (struct inode *, struct file *);
在文件结构被释放时引用这个操做.如同open, release能够为NULL.
int (*fsync) (struct file *, struct dentry *, int);
这个方法是fsync系统调用的后端,用户调用来刷新任何挂着的数据.若是这个指针是NULL,系统调用返回-EINVAL.
int (*aio_fsync)(struct kiocb *, int);
这是fsync方法的异步版本.
int (*fasync) (int, struct file *, int);
这个操做用来通知设备它的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.
int (*check_flags)(int)
这个方法容许模块检查传递给fnctl(F_SETFL...)调用的标志.
int (*dir_notify)(struct file *, unsigned long);
这个方法在应用程序使用fcntl来请求目录改变通知时调用.只对文件系统有用;驱动不须要实现dir_notify.
---------------------------------------------------------------------
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_structf_owner;
unsigned int f_uid, f_gid;
struct file_ra_statef_ra;
unsigned long f_version;
void *f_security;
/* 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;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
文件结构体表明一个打开的文件,系统中的每一个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时建立,并传递给在文件上进行操做的任何函数。在文件的全部实例都关闭后,内核释放这个数据结构。在内核建立和驱动源码中,struct file的指针一般被命名为file或filp。一下是对结构中的每一个数据成员的解释:
1、
union {
struct list_head fu_list;
struct rcu_head rcuhead;
}f_u;
其中的struct list_head定义在linux/include/linux/list.h中,原型为:
struct list_head {
struct list_head *next, *prev;
};
用于通用文件对象链表的指针。
struct rcu_head定义在linux/include/linux/rcupdate.h中,其原型为:
/**
* struct rcu_head - callback structure for use with RCU
* @next: next update requests in a list
* @func: actual update function to call after the grace period.
*/
struct rcu_head {
struct rcu_head *next;
void (*func)(struct rcu_head *head);
};
RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制,具体在这里有介绍:
http://www.ibm.com/developerworks/cn/linux/l-rcu/
2、
struct path f_path;
被定义在linux/include/linux/namei.h中,其原型为:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
在早些版本的内核中并无此结构,而是直接将path的两个数据成员做为struct file的数据成员,
struct vfsmount *mnt的做用是指出该文件的已安装的文件系统,
struct dentry *dentry是与文件相关的目录项对象。
3、
const struct file_operations *f_op;
被定义在linux/include/linux/fs.h中,其中包含着与文件关联的操做,如:
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
等。当打开一个文件时,内核就建立一个与该文件相关联的struct file结构,其中的*f_op就指向的是
具体对该文件进行操做的函数。例如用户调用系统调用read来读取该文件的内容时,那么系统调用read最终会陷入内核调用sys_read函数,而sys_read最终会调用于该文件关联的struct file结构中的f_op->read函数对文件内容进行读取。
4、
atomic_t f_count;
atomic_t被定义为:
typedef struct { volatile int counter; } atomic_t;
volatile修饰字段告诉gcc不要对该类型的数据作优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。
本质是int类型,之因此这样写是让编译器对基于该类型变量的操做进行严格的类型检查。此处f_count的做用是记录对文件对象的引用计数,也即当前有多少个进程在使用该文件。
5、
unsigned int f_flags;
当打开文件时指定的标志,对应系统调用open的int flags参数。驱动程序为了支持非阻塞型操做须要检查这个标志。
6、
mode_t f_mode;
对文件的读写模式,对应系统调用open的mod_t mode参数。若是驱动程序须要这个值,能够直接读取这个字段。
mod_t被定义为:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t mode_t;
7、
loff_t f_pos;
当前的文件指针位置,即文件的读写位置。
loff_t被定义为:
typedef long long __kernel_loff_t;
typedef __kernel_loff_t loff_t;
8、
struct fown_struct f_owner;
struct fown_struct在linux/include/linux/fs.h被定义,原型为:
struct fown_struct {
rwlock_t lock; /* protects pid, uid, euid fields */
struct pid *pid; /* pid or -pgrp where SIGIO should be sent */
enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
uid_t uid, euid; /* uid/euid of process setting the owner */
int signum; /* posix.1b rt signal to be delivered on IO */
};
该结构的做用是经过信号进行I/O时间通知的数据。
9、
unsigned int f_uid, f_gid;
标识文件的全部者id,全部者所在组的id.
10、
struct file_ra_state f_ra;
struct file_ra_state结构被定义在/linux/include/linux/fs.h中,原型为:
struct file_ra_state {
pgoff_t start; /* where readahead started */
unsigned long size; /* # of readahead pages */
unsigned long async_size; /* do asynchronous readahead when
there are only # of pages ahead */
unsigned long ra_pages; /* Maximum readahead window */
unsigned long mmap_hit; /* Cache hit stat for mmap accesses */
unsigned long mmap_miss; /* Cache miss stat for mmap accesses */
unsigned long prev_index; /* Cache last read() position */
unsigned int prev_offset; /* Offset where last read() ended in a page */
};
文件预读状态,文件预读算法使用的主要数据结构,当打开一个文件时,f_ra中出了perv_page(默认为-1)和ra_apges(对该文件容许的最大预读量)这两个字段外,其余的全部西端都置为0。
11、
unsigned long f_version;
记录文件的版本号,每次使用后都自动递增。
12、
#ifdef CONFIG_SECURITY
void *f_security;
------------------------------------------------------------------------------------------------------
struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino;
atomic_t i_count;
umode_t i_mode;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;