linux驱动程序中最重要的涉及3个重要的内核数据结构,分别为file_operations,file和inode。
在linux中inode结构用于表示文件,而file结构则表示打开的文件的描述,因为对于单个文件而言可能会有许多个表示打开的文件的描述符,因而就可能会的对应有多个file结构,但是都指向单个inode结构。
在系统内部,I/O设备的存取操作通过特定的的入口来进行,而这组特定的入口由驱动程序来提供的。通常这组设备驱动的接口是由结构体file_operations向系统说明的,它定义在include/linux/fs.h中。
file_operations的数据结构如下:(2.6内核)
struct file_operations{
struct module *owner;//拥有该模块的指针,一般THIS_MODULE
loff_t (*llseek) (struct file*, loff_t,int);//修改文件当前的读写位置
ssize_t (*read)(struct file *,char *, size_t, loff_t *);
//从设备同步读取数据
ssize_t (*write)(struct file *,const char *, size_t, loff_t *);
//向设备发送数据
int (*readdir) (struct file *, void *, filldir_t);
//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll)(struct file *, struct poll_table_struct *);
//轮询函数,判断目前是否可以进行非阻塞的读取或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
//执行设备IO控制命令
int (*mmap) (sturct file *, struct vm_area_struct*);
//用于请求将设备内存映射到进程地址空间
int (*open) (struct inode *, struct file *);//打开
int (*flush)(struct file *);
int (*release)(struct inode *, struct file *);//关闭
int (*synch)(struct file *, struct dentry *, int datasync);//刷新待处理的数据
int (*fasync)(int, struct file *, int);//通知设备fasync标志发生变化
int (*lock)(struct file *, int, struct file_lock);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long*, loff_t * );
sszie_t(*writev)(struct file *, const struct iovec *, unsigned long *,
loff_t *);//分散聚集型的读写
ssize_t (*sengpage)(struct file *, struct page *, int, size_t,loff_t *, int);
unsigned long (*get_unmaapped_area)(struct file *,unsigned long, unsigned long, unsigned long, unsigned long );
long (*fcntl)(int fd, unsigned int cmd,unsigned arg, struct file *filp);
};
随着内核的不断升级,file_operations结构也会越来越大,不同版本会稍有不同。
一般的设备驱动程序只需用到上面的蓝色部分的五个函数!
Linux的设备文件是同硬件一一对应的,因而对设备的操作可以通过对设备文件的操作来实现。而这些操作的实现其实就是对一些标准的系统调用,如open(),
read(),write(),close()等。实际上file_operations就是把系统调用和驱动程序关联起来的关键数据结构。这个结构的每一成员都对应着一个系统调用。当用户进程利用系统调用对设备进行读写操作的时候,这些系统调用通过设备的主设备号和次设备号来确定相应的驱动程序,然后读取file_operations中相应的函数指针,接着把控制权交给函数,从而完成linux设备驱动程序的工作。
struct file提供关于被打开的文件信息,主要供与文件系统对应的设备文件驱动程序使用。结构如下:
struct file{
mode_t f_mode;//表示文件是否可读或可写,FMODE_READ或FMODE_WRITE
dev_ t f_rdev ;// 用于/dev/tty
off_t f_ops;//当前文件位移
unsigned short f_flags;//文件标志,O_RDONLY,O_NONBLOCK和O_SYNC
unsigned short f_count;//打开的文件数目
unsigned short f_reada;
struct inode *f_inode;//指向inode的结构指针
struct file_operations *f_op;//文件索引指针
}
还有 struct device_struct
在系统启动过程中的块设备和字符设备管理表的定义在文件fs/device.h中
struct device_struct
{
const char *name;
struct file_operations *fops;
}
static struct device_struct chrdevs[MAX_CHRDEV];
static struct device_struct blkdevs[MAX_BLKDEV];
其实块设备表和字符设备表使用了相同的数据结构。这些设备表也称作设备开关表,不同的是他们定义了一组函数指针对设备进行管理。而这里系统用文件操作(file_operations)代替了那组开关。文件操作是文件系统与驱动程序之间的接口,系统特殊文件在建立的时候并没有把两者对应起来,只是把设备的默认文件结构和i节点结构赋给设备文件,而真正的对应定义在系统启动后,当设备被打开时才进行的。
三者之间关系:
struct file_operations是struct file的一个域,我们在使用系统调用open()打开一个设备节点struct inode时,我们会得到一个文件struct file,同时返回一个文件描述符,该文件描述符是一个整数,我们称之为句柄,通过访问句柄我们能够访问设备文件struct file,描述符是一个有着特殊含义的整数,特定位都有一定的意义或属性。