file_operations结构
file_operations结构体成员函数是字符设备驱动程序设计的主体内容,这些函数实际上会在应用程序执行open、close、read、write的时候被最终调用。定义在<linux/fs.h>中,重要成员如下。
struct file_operations {
struct module *owner; // 拥有该结构的模块的指针,通常都是被定义为THIS_MODULE
loff_t (*llseek) (struct file *, loff_t, int); // 用来修改文件当前读写的位置,并将新位置作为返回值返回
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); // 从设备中读取数据,当被负值为NULL的时候,read系统调用将出错,返回-EINVAL
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); // 向设备中发送数据
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *); // 是poll、epoll、select这三个系统调用的后端实现。用来查询某个或者多个文件描述符的读取或写入是否会阻塞。如果驱动程序将poll定义为NULL,则设备会被认为既可以读也可以写,并且不会阻塞
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); // 原ioctl,现被unlocked_ioctl取代,二者的区别在于unlocked_ioctl参数中少了inode,二者是兼容的
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *); // 打开设备,如果设置为NULL,则设备的打开操作永远成功,但是系统不会通知驱动程序
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *); // 关闭设备,通open一样也可以设定为NULL
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
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 (*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 **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
file结构
定义在<linux/fs.h>中,file结构是一个内核结构,它不会出现在用户空间中。它由内核在open时创建,并传递给在该文件上进行操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会释放这个数据结构。
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op; // 与文件相关的操作,内核在执行open的时候为这个指针赋值,以后需要处理这些操作的时候就读取这个指针。
/*
* Protects f_ep, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
enum rw_hint f_write_hint;
atomic_long_t f_count;
unsigned int f_flags; // 文件标志,例如O_RDONLY、O_NONBLOCK和O_SYNC。为了检查用户请求是否是阻塞式操作,需要检查O_NONBLOCK标志,其他标志很少用到。注意检查读写权限应该用f_mode而不是f_flags。
fmode_t f_mode; // 文件模式,通过FMODE_READ和FMODE_WRITE来标识文件是否可读或者可写。内核在调用驱动程序的read和write前已经检查了访问权限,驱动程序无需为此作额外的判断
struct mutex f_pos_lock;
loff_t f_pos; // 当前读写的位置。loff_t是一个64位的数(long long)如果驱动程序需要直到文件中当前的位置,可以读取这个值,但是不要去修改它。
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data; // 在open的时候,这个指针会被置为NULL,驱动程序可以将这个字段用于任何目的或者忽略这个字段。
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct hlist_head *f_ep;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
errseq_t f_sb_err; /* for syncfs */
} __randomize_layout
inode结构
内核用inode结构在内部表示文件,与file结构不同,file结构表示的是打开的文件描述符。
dev_t i_rdev;
对表示设备文件的inode结构,该字段包含了真正的设备编号。
可以通过下面的函数来获取主次设备号
次设备号:unsigned int iminor(struct inode *inode);
主设备号:unsigned int imajor(struct inode *inode);
struct cdev *i_cdev;
表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。