1.字符设备驱动中重要的数据结构
大部分的基础性的驱动操作包括 3 个重要的内核数据结构, 他们分别是file_operations, file, 和 inode 。需要对这些结构的基本了解才能够做更多的事情。
2.文件操作(file_operations)
struct file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,这个结构的每一个成员都对应着一个系统调用,用来执行一系列的系统调用。
内核5.10.6中的file_operations如下:
struct file_operations {
struct module *owner;
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 (*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 *);
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 *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
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;
3.文件结构(file)
struct file代表一个打开的文件,存放了文件所有的动态信息,在执行file_operation中的open操作时被创建,每打开一次就会创建一次,并传递给在文件上操作的任何函数, 直到最后的关闭.。在文件的所有实例都关闭后, 内核释放这个数据结构。
需要注意 file 与用户空间程序的 FILE 指针没有任何关系。 一个 FILE 定义在 C 库中, 从不出现在内核代码中.;一个 struct file,是一个内核结构,从不出现在用户程序中。
内核5.10.6中的file如下:
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;
/*
* Protects f_ep_links, 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;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
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;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
errseq_t f_sb_err; /* for syncfs */
} __randomize_layout
__attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
4.inode结构(inode)
struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件。(可以理解为inode中存放的是文件的静态信息)
内核5.10.6中的inode如下(重要成员):
struct inode {
dev_t i_rdev;//这个成员包含实际的设备编号
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;//是内核的内部结构, 代表字符设备
char *i_link;
unsigned i_dir_seq;
};
推荐使用以下这些宏代替直接操作 i_rdev:
MKDEV(ma,mi) :根据主次设备号合成设备号
MAJOR(dev) :得到主设备号(dev设备号)
MINOR(dev):得到次设备号(dev设备号)
下图为可视化构建: