file_operations
是 Linux 内核中用于定义字符设备驱动与用户空间交互接口的核心结构体。它通过函数指针数组,将用户空间的系统调用(如 open()
, read()
, write()
等)映射到内核驱动的具体实现函数。以下是其详细说明:
1. 结构体定义
在较新的内核版本(如 5.x+)中,file_operations
定义在 <linux/fs.h>
中,典型成员如下:
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 (*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 (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
// ... 其他成员(根据内核版本可能不同)
};
2. 核心成员详解
(1) owner
- 作用:指向模块所有者的指针,通常设为
THIS_MODULE
。 - 用途:防止模块被卸载时仍有未释放的引用(通过
try_module_get()
和module_put()
管理引用计数)。 - 示例:
static struct file_operations fops = { .owner = THIS_MODULE, // 其他成员... };
(2) open
- 触发条件:用户调用
open()
或fopen()
。 - 原型:
int (*open)(struct inode *inode, struct file *filp);
- 参数:
inode
:设备文件的 inode 结构体,包含设备号等信息。filp
:文件对象,可通过filp->private_data
存储驱动私有数据。
- 典型操作:
- 初始化设备。
- 分配资源(如内存、DMA 缓冲区)。
- 设置
filp->private_data
指向驱动上下文。
(3) release
- 触发条件:用户调用
close()
或fclose()
。 - 原型:
int (*release)(struct inode *inode, struct file *filp);
- 用途:
- 释放
open()
中分配的资源。 - 关闭硬件设备。
- 清理
filp->private_data
。
- 释放
(4) read
/ write
- 触发条件:用户调用
read()
或write()
。 - 原型:
ssize_t (*read)(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); ssize_t (*write)(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
- 参数:
buf
:用户空间缓冲区地址(需通过copy_to_user()
/copy_from_user()
访问)。count
:请求的字节数。f_pos
:文件偏移量(由驱动维护,如*f_pos += ret
)。
- 返回值:成功时返回实际操作的字节数,错误返回负值(如
-EINVAL
)。
(5) unlocked_ioctl
/ compat_ioctl
- 触发条件:用户调用
ioctl()
。原型:long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg); long (*compat_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg);
- 实现设备特定的控制命令(如配置寄存器、获取状态)。
compat_ioctl
用于 64 位内核处理 32 位用户的ioctl
调用。
- 实现要点:
- 使用
_IOC_*
宏定义命令(如_IOC_READ
,_IOC_WRITE
)。 - 通过
copy_to_user()
/copy_from_user()
传递参数。
- 使用
(6) poll
- 触发条件:用户调用
select()
,poll()
,epoll()
。 - 原型:
__poll_t (*poll)(struct file *filp, struct poll_table_struct *wait);
- 用途:
- 实现非阻塞 I/O。
- 注册等待队列(
poll_wait(filp, &queue, wait)
),当数据就绪时唤醒进程。
- 返回值:位掩码(如
POLLIN
表示可读,POLLOUT
表示可写)。
(7) mmap
- 触发条件:用户调用
mmap()
。 - 原型:
int (*mmap)(struct file *filp, struct vm_area_struct *vma);
- 用途:
- 将设备内存映射到用户空间(如摄像头帧缓冲)。
- 需设置
vma->vm_flags
并调用remap_pfn_range()
。
(8) llseek
- 触发条件:用户调用
lseek()
。 - 原型:
loff_t (*llseek)(struct file *filp, loff_t offset, int whence);
- 用途:
- 修改文件读写位置(如随机访问设备)。
- 返回新位置,错误返回负值。
3. 同步与并发控制
- 问题:多个进程同时访问设备可能导致数据竞争。
- 解决方案:
- 使用互斥锁(
mutex
)保护共享资源。 - 在
open()
中初始化锁,release()
中销毁。 - 示例:
static int my_open(struct inode *inode, struct file *filp) { struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev); mutex_lock(&dev->lock); filp->private_data = dev; // 初始化操作... mutex_unlock(&dev->lock); return 0; }
- 使用互斥锁(
4. 示例:简单字符设备驱动
#include <linux/module.h>
#include <linux/fs.h>
static int dev_open(struct inode *inode, struct file *filp) {
// 初始化设备
return 0;
}
static ssize_t dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
// 从设备读取数据到用户空间
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = dev_open,
.read = dev_read,
// 其他成员...
};
static int __init my_init(void) {
// 注册字符设备
return 0;
}
static void __exit my_exit(void) {
// 注销设备
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
5. 注意事项
- 错误处理:所有函数需正确返回错误码(如
-EFAULT
,-EINVAL
)。 - 资源管理:在
open()
中分配资源,release()
中释放。 - 内核版本兼容性:部分成员(如
read_iter
/write_iter
)用于替代传统read
/write
,支持零拷贝。 - 文档参考:内核源码
Documentation/driver-model/
和Documentation/filesystems/vfs.txt
。
通过合理实现 file_operations
,驱动可高效、安全地与用户空间交互。