file_operations 结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在应用程序进行 Linux 的 open()、write()、read()、close() 等系统调用时最终被调用。file_operations 结构体目前已经比较庞大,它的定义如下:
定义在 linux/include/linux/fs.h
struct file_operations {
struct module *owner;
//拥有该结构的模块的指针,一般为 THIS_MODULES
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 *);
// 向设备发送数据
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
//初始化一个异步的读取操作
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 *);
// 轮询函数,判断目前是否可以进行非阻塞的读取或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
// 执行设备 I/O 控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//不使用BLK 文件系统,将使用此种函数指针代替 ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
// 在64 系统上,32位的 ioctl调用将使用此函数指针代替
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);
//这个方法是 fsync 系统调用的后端, 用户调用来刷新任何挂着的数据. 如果这个指针是 NULL, 系统调用返回 -EINVAL
int (*aio_fsync) (struct kiocb *, int datasync);
//异步 fsync
int (*fasync) (int, struct file *, int);
//通知设备 FASYNC 标志发生变化
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);
// 在进程地址空间找到一个将底层设备中的内存段映射的位置
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);
int (*setlease)(struct file *, long, struct file_lock **);
};
llseek() 函数用来修改一个文件的当前读写位置,并将新位置返回,在出错时,这个函数返回一个负值。
read() 函数用来从设备中读取数据,成功时函数返回读取字节数,出错时返回一个负值
write() 函数向设备发送数据,成功时该函数返回写入的字节数。如果此函数未被实现,当用户进行 write() 系统调用时,将得到 -EINVAL 返回值
readdir() 函数仅用于目录,设备点不需要实现它
ioctl() 函数提供设备相关控制命令的实现(既不是读操作也不是写操作),当调用成功时,返回给调用程序一个非负值。内核本身识别部分控制命令,而不必调用设备驱动中的 ioctl()。如果设备不提供 ioctl() 函数,对于内核不能识别的命令,用户进行 ioctl() 系统调用时将获得 -EINVAL 返回值。
mmap() 函数将设备内存映射到进程内存中,如果设备驱动未实现此函数,用户进行 mmap()系统调用时将获得 -ENODEV 返回值。这个函数对于帧缓冲等设备特别有意义。
当用户空间调用 Linux API 函数 open() 打开设备文件时,设备驱动的 open() 函数最终被调用。驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功。与 open() 函数对应的是 release() 函数。
poll() 函数一般用于询问设备是否可被非阻塞地立即读写。当询问的条件未触发时,用户空间进行 select() 和 poll() 系统调用将引起进程的阻塞。
aio_read() 和 aio_write() 函数分别对与文件描述符对应的设备进行异步读、写操作。设备实现这两个函数后,用户空间可以对该设备文件描述符调用 aio_read()、aio_write() 等系统调用进行读写。
http://vicyliu1984.blog.163.com/blog/static/31541232201010773844198/