linux 驱动程序file_operations结构体学习

linux  驱动程序file_operations结构体学习

一、file_operations结构体的作用是用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。里面的成员函数是字符设备驱动程序设计的主体内容,当应用程序进行open()、write()、read()等系统调用时候内核会调用到这些函数。

二、file_operations结构体是在kernel/include/linux/fs.h里面定义的。具体内容因版本不同略有差异,下面是内核3.10版本的内容。


三、现在我们对主要函数进行分析。

struct file_operations{
	struct module *owner
	//第一个 file_operations 成员根本不是一个操作; 它是一个指向拥有这个结构的模块的指针. 这个成员

用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 

<linux/module.h> 中定义的宏.

	loff_t (*llseek) (struct file *, loff_t, int);
	//llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一

个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 

NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述).

	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	//用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid 

argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平

台本地的整数类型).

	ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);
	//初始化一个异步读 -- 可能在函数返回前不结束的读操作. 如果这个方法是 NULL, 所有的操作会由 

read 代替进行(同步地).

	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	//发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成

功写的字节数.

	ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);
	//初始化设备上的一个异步写.

	int (*readdir) (struct file *, void *, filldir_t);
	//对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用.

	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	//poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用作查询对一个或多个文件描述符的

读或写是否会阻塞. poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 并且, 可能地, 提供给内核

信息用来使调用进程睡眠直到 I/O 变为可能. 如果一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写.

	int (*unlocked_ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	//提供设备相关控制命令的实现,这个命令既不是读操作也不是写操作,当调用成功是返回一个非负值。ioctl 系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外

, 几个 ioctl 命令被内核识别而不必引用 fops 表. 如果设备不提供 ioctl 方法, 对于任何未事先定义的请求(-

ENOTTY, "设备无这样的 ioctl"), 系统调用返回一个错误.

	int (*mmap) (struct file *, struct vm_area_struct *);
	//mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -

ENODEV.

	int (*open) (struct inode *, struct file *);
	//尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设

备打开一直成功, 但是你的驱动不会得到通知.

	int (*flush) (struct file *);
	//flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待)设备的任何未完成的操

作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用; SCSI 磁带驱动使用它, 

例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求.

	int (*release) (struct inode *, struct file *);
	//在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.

	int (*fsync) (struct file *, struct dentry *, int);
	//这个方法是 fsync 系统调用的后端, 用户调用来刷新任何挂着的数据. 如果这个指针是 NULL, 系统调

用返回 -EINVAL.

	int (*aio_fsync)(struct kiocb *, int);
	//这是 fsync 方法的异步版本.

	int (*fasync) (int, struct file *, int);
	//这个操作用来通知设备它的 FASYNC 标志的改变. 异步通知是一个高级的主题, 在第 6 章中描述. 这个

成员可以是NULL 如果驱动不支持异步通知.

	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);
	//这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存段中. 这个任务通常由

内存管理代码进行; 这个方法存在为了使驱动能强制特殊设备可能有的任何的对齐请求. 大部分驱动可以置这个方法

为 NULL.[10]

	int (*check_flags)(int)
	//这个方法允许模块检查传递给 fnctl(F_SETFL...) 调用的标志.

	
}

四、实际使用的一个实例。

1、用户空间代码。


2、内核空间代码。



  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`file_operations` 结构体Linux 内核中用来实现字符设备驱动的关键数据结构之一。它定义了一组函数指针,用于实现对字符设备的操作,包括打开设备、关闭设备、读取数据、写入数据、控制设备等等。具体来说,`file_operations` 结构体中定义的函数指针包括以下方法: - `open`: 打开设备调用的方法。 - `release`: 关闭设备调用的方法。 - `read`: 读取设备数据调用的方法。 - `write`: 写入设备数据调用的方法。 - `ioctl`: 控制设备调用的方法。 - `mmap`: 内存映射调用的方法。 这些方法实现了对字符设备的各种操作,可以根据具体的需求进行自定义实现。例如,`read` 方法可以从设备中读取数据并返回给应用程序,`write` 方法可以将应用程序传递的数据写入设备等等。在实现这些方法,需要注意遵守一些规则和限制,例如不允许阻塞式操作等等。 需要注意的是,`file_operations` 结构体是在字符设备驱动中定义的,用于告诉内核如何操作设备。在注册字符设备,需要将 `file_operations` 结构体作为参数传递给 `register_chrdev()` 函数。例如: ``` struct file_operations my_fops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, .read = my_read, .write = my_write, .ioctl = my_ioctl, }; register_chrdev(my_major, "my_device", &my_fops); ``` 在上述代码中,`my_fops` 是自定义的 `file_operations` 结构体,包含了各种操作设备的方法。`register_chrdev()` 函数用于注册字符设备,并将 `my_fops` 作为参数传递给该函数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值