linux 内核重要数据结构 file_operations、file、inode

标记化结构初始化方法

内核中结构体的赋值常使用标准C的标记化结构初始化方法,这在之前的用户层代码并不常见。

static struct file_operations fops ={
        .owner = THIS_MODULE,
        .open  = hello_dev_open,
        .release = hello_dev_release,
        .read = hello_dev_read,
        .write = hello_dev_write,
};

优点有以下三点:

  1. 首先,标记传参不用理会参数传递的顺序,程序员不用记忆参数的顺序;
  2. 我们可以选择性传参,在传统C语言顺序传参中,如果你只想对第三个变量进行初始化,那么你不得不给第一个, 第二个参数进行初始化,而有时候一个变量并没有很合适的默认值,而使用标记初始化法,你可以相当自由地对你有把握的参数进行初始化;
  3. 扩展性更好,如果你要在该结构体中增加一个字段,传统方式下,为了考虑代码修改量,你最好将新添加的字段放在这个结构体的最后面,否则你将要面对大量且无趣的修改。

file_operation

《LINUX设备驱动程序》第三版P53~55

该结构体为一个函数指针(回调函数)的集合。用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。

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 *);//从设备中同步读取数据。用户层调用read,直接转到该函数指针指向的函数   
   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); //刷新待处理的数据   
  int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据   
  int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化   
  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 **);   
};  

常用的有(我的github有例程)open,release,read,write,mmap

file

《LINUX设备驱动程序》第三版P57~59

区别用户空间的file。FILE在用户空间定义且不会出现在内核代码中,struct file是一个内核结构,不会出现在用户程序中。file结构代表一个打开的文件,由内核在open时创建,并传递给在该文件上进行操作的所有函数。file_operations内的回调函数的第一个参数都是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; 	//文件标志,如O_RDONLY,O_NONBLOCK,O_SYNC检查用户请求是否是非阻塞的操作
	fmode_t			f_mode;  //文件模式 通过FMODE_READ FMODE_WRITE表示文件可读或可写
	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;  
    /*open系统调用在调用open驱动方法前把这个指针置为NULL,驱动程序可以把这个指针用于任何目的或者忽略他。
    但是一定要在内核销毁file结构前在release方法中释放内存。这是保存状态信息非常有用的资源。*/
 
#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;
} __randomize_layout

重要的有 f_pos

inode

内核用inode结构在内部表示文件,它和file不同,后者表示打开的文件描述符,对于单个文件,可能会有许多表示打开的文件描述符的file结构,但它们都指向单个inode结构。

inode结构中包含了大量有关文件的信息,作为常规,只有下面两个字段对驱动程序有用:

dev_t   i_rdev;  //对表示设备文件的inode结构,包含了真正的设备编号。
struct cdev *i_cdev;//表示字符设备的内核内部数据结构,当inode指向一个字符设备文件,该字段包含指向struct cdev结构的指针

下面两个宏可用来从inode中获得主设备号和次设备号:

unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值