这两个是 Linux 内核中 VFS(虚拟文件系统)层的核心结构体,驱动开发者不需要自己定义它们,而是由内核在调用驱动函数时自动传入。
两者对比
| 属性 | struct inode | struct file |
|---|---|---|
| 含义 | 文件的元数据 | 文件的打开实例 |
| 生命周期 | 内核中唯一(所有进程共享) | 每次 open() 创建一个 |
| 数量关系 | 一个文件对应一个 inode | 同一文件可被多个进程打开,产生多个 file |
| 存储内容 | 设备号、文件大小、权限、时间戳等 | 文件位置指针、打开模式、私有数据等 |
struct inode — 文件元数据
// 内核源码:include/linux/fs.h
struct inode {
umode_t i_mode; /* 文件权限和类型 */
kuid_t i_uid; /* 文件所有者 UID */
kgid_t i_gid; /* 文件所有者 GID */
loff_t i_size; /* 文件大小 */
struct timespec i_atime; /* 最后访问时间 */
struct timespec i_mtime; /* 最后修改时间 */
struct timespec i_ctime; /* 最后状态改变时间 */
unsigned long i_ino; /* inode 号(唯一标识) */
dev_t i_rdev; /* 真实设备号(对设备文件有意义) */
union {
struct cdev *i_cdev; /* 指向字符设备结构体的指针 */
struct block_device *i_bdev;
};
// ... 其他字段
};
驱动中最常用:
// 从 inode 获取主次设备号(设备文件的设备号)
dev_t dev_num = inode->i_rdev;
// 从 inode 获取 cdev 结构体(内核内部用)
struct cdev *cdev = inode->i_cdev;
struct file — 打开的文件实例
// 内核源码:include/linux/fs.h
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path; /* 路径信息 */
struct inode *f_inode; /* 关联的 inode */
const struct file_operations *f_op; /* 文件操作集(驱动实现) */
void *private_data;/* 驱动私有数据指针 */
loff_t f_pos; /* 当前文件读写位置 */
unsigned int f_flags; /* open() 时的标志 (O_RDWR 等) */
fmode_t f_mode; /* 文件打开模式 */
atomic_long_t f_count; /* 引用计数 */
// ... 其他字段
};
驱动中最常用:
file->private_data /* 存储驱动自定义的设备结构体指针 */
file->f_pos /* 当前读写位置(对字符设备通常不用管) */
file->f_flags /* O_RDWR、O_NONBLOCK 等 */
为什么驱动里看不到它们定义,却能直接用?
因为它们是由内核在调用时自动创建并传递的:
用户空间
open("/dev/xxx") 用户调用 open()
│
▼
内核 VFS 层
alloc inode + file 内核分配结构体
│
▼
chrdev_open(inode, file) 内核调用驱动的 open 函数
│ ← 你在这里收到这两个结构体
▼
read/write/ioctl 内核继续调用其他驱动函数
│
▼
file_release() 关闭时内核调用
│
▼
内核 VFS 层
iput inode + fput file 释放结构体
在驱动中的实际用法
/* open 函数:初始化私有数据 */
static int chrdev_open(struct inode *inode, struct file *file)
{
/* 从 inode 提取设备号(通过 inode->i_cdev 反向找到我们的 cdev) */
struct cdev *cdev = inode->i_cdev;
struct device_test *dev = container_of(cdev, struct device_test, cdev_test);
/* 将设备结构体存入 file 的私有数据,后续 read/write 可以直接取出 */
file->private_data = dev;
printk(KERN_INFO "Device opened\n");
return 0;
}
/* read 函数:从 private_data 取出设备信息 */
static ssize_t chrdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct device_test *dev = file->private_data; /* 取出之前存的结构体指针 */
/* 用 dev 做事... */
return 0;
}
/* release 函数:清理 */
static int chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device closed\n");
return 0;
}
核心流程总结
open() 被调用
│
├─ 内核分配 struct inode(存放设备号等元数据)
├─ 内核分配 struct file(存放打开状态等)
│
├─ 传入驱动 chrdev_open(inode, file)
│ └─ file->private_data = &my_dev ← 绑定设备
│
├─ 传入驱动 chrdev_read/write/ioctl(file, ...)
│ └─ 从 file->private_data 取出设备 → 操作硬件
│
└─ close() 时
└─ 传入驱动 chrdev_release(inode, file)
简单说:inode 告诉你”打开的是什么设备”,file 告诉你”这次打开的会话状态”。驱动通过 file->private_data 将自己的设备结构体和每一次打开操作关联起来