一、存共享硬件对象
cpp
static int my_open(struct inode *inode, struct file *file)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
file->private_data = dev; // 存共享硬件对象,省去后续 container_of
return 0;
}
static long my_ioctl(struct file *file, ...)
{
struct my_dev *dev = file->private_data; // 直接使用,O(1) 访问
}
典型例子:RTC 驱动
cpp
static int rtc_dev_open(struct inode *inode, struct file *file)
{
struct rtc_device *rtc = container_of(inode->i_cdev, struct rtc_device, cdev);
file->private_data = rtc; // 所有 fd 共享同一个 rtc_device
return 0;
}
-
读取 RTC 时间,硬件只有一个"当前时间",读 10 次都一样
-
不需要记录"我这个 fd 读到了第几个字节"
-
rtc->ops->read_time()是纯函数,无副作用
二、存本文件私有结构
cpp
struct my_file_data {
int pos; // 本文件的读写位置
int flags; // 本文件的 O_NONBLOCK 等标志
wait_queue_head_t wait; // 本文件的等待队列
struct my_dev *dev; // 再存一份硬件指针(可选)
};
static int my_open(struct inode *inode, struct file *file)
{
struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);
struct my_file_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
data->pos = 0;
data->flags = file->f_flags; // 保存打开标志
init_waitqueue_head(&data->wait);
data->dev = dev; // 同时存硬件指针,方便后续使用
file->private_data = data; // 每个 fd 独占一份
return 0;
}
static ssize_t my_read(struct file *file, ...)
{
struct my_file_data *data = file->private_data;
*ppos = data->pos; // 只影响本文件,不影响别的 fd
data->pos += len;
}
static int my_release(struct inode *inode, struct file *file)
{
kfree(file->private_data); // 释放本文件独占内存
}
原因:
-
读/写/位置是文件描述符的属性,不是硬件属性
-
fd1 读到 100 字节,fd2 应该从 0 开始读,不能共享
pos
优点:
-
每个 fd 完全独立,互不干扰
-
可以实现复杂的流控、异步通知
缺点:
-
需要
kmalloc/kfree,有内存泄漏风险 -
release()必须配对释放
三、linux 驱动程序开发属性的绑定
属性确实可以绑在 4 个不同层次 (设备、驱动、总线、类),RTC 选择类(class)绑定 是因为它是通用框架 ,而某些驱动选择设备(device)绑定 是因为它们有私有属性
1. 类层级绑定(RTC 的用法)
cpp
代码位置:drivers/rtc/class.c
rtc_class->dev_groups = rtc_groups; // 所有 rtc 设备自动继承
效果:
所有 struct rtc_device 都会长出 time、date、wakealarm 等文件
驱动 不写一行代码,属性就自动有了
属性是 "通用属性" ,与具体芯片无关
适用场景:
子系统框架(RTC、input、video、sound)
属性是所有同类设备共有的(如 RTC 都必须能读时间)
2. 设备层级绑定(驱动私有属性)
代码位置 :某个具体驱动的 probe() 里
cpp
static int my_sensor_probe(struct i2c_client *client, ...)
{
struct device *dev = &client->dev;
// 创建驱动私有的属性
device_create_file(dev, &dev_attr_custom_reg);
device_create_file(dev, &dev_attr_sampling_rate);
return 0;
}
效果:
只有 这个设备 有 custom_reg 文件
其他同芯片的驱动不会自动继承
属性是 "私有属性" ,只有这个驱动需要
适用场景:
某个芯片有特殊寄存器(如校准值、工作模式)
属性只对这个设备有意义
3. 驱动层级绑定(driver 属性)
代码位置 :struct device_driver 定义时
cpp
static struct driver_attribute drv_attr_version = __ATTR_RW(version);
static struct attribute *sensor_driver_attrs[] = {
&drv_attr_version.attr,
NULL
};
ATTRIBUTE_GROUPS(sensor_driver);
static struct platform_driver sensor_driver = {
.driver = {
.name = "sensor",
.groups = sensor_driver_groups, // 绑在 driver 上
},
.probe = sensor_probe,
};
效果:
属性出现在 /sys/bus/platform/drivers/sensor/ 目录下
与 具体设备实例无关(没 probe 也能看到)
存的是驱动版本号、固件信息等全局信息
适用场景:
驱动自身的元信息(版本、编译时间、许可证)
调试开关(影响所有设备实例)
4、一张图对比三者的作用域
cpp
/sys/
├── class/rtc/ ← 类绑定(通用属性)
│ └── rtc0/
│ ├── time ← 所有 RTC 都有
│ ├── date
│ └── wakealarm
│
├── bus/i2c/devices/ ← 设备绑定(私有属性)
│ └── 1-0050/
│ ├── name
│ └── custom_reg ← 只有这个传感器有
│
└── bus/platform/drivers/my-sensor/ ← 驱动绑定(驱动级属性)
└── version ← 驱动版本信息
5. 一句话总结
类绑定是"批发"(所有设备自动有),设备绑定是"零售"(单个设备自己加),驱动绑定是"品牌标签"(驱动自己的信息)。RTC 用类绑定因为它是个框架,具体芯片驱动用设备绑定加私有寄存器。
1. 基本用法
cpp
#include <linux/io.h>
// 错误做法:可能被优化掉
u32 value = *reg; // 编译器可能认为没变化,直接复用旧值
// 正确做法:强制读
u32 value = readl(reg);
readb(addr) 8 位 (byte) 读 1 字节寄存器
readw(addr) 16 位 (word) 读 2 字节寄存器
readl(addr) 32 位 (long) 读 4 字节寄存器
readq(addr) 64 位 (quad) 读 8 字节寄存器(64 位系统)
u32 value = readl(register_address);
等价于:
u32 value = *(volatile u32 __iomem *)register_address;
cpp
#include <linux/io.h> // readl/writel
#include <linux/ioport.h> // request_mem_region
#include <linux/of_address.h> // of_iomap (设备树)
struct my_dev {
void __iomem *regs; // 寄存器基地址
};
static int my_probe(...)
{
// 方式 A:设备树获取并映射
dev->regs = of_iomap(pdev->dev.of_node, 0);
// 方式 B:物理地址硬编码
dev->regs = ioremap(0x10003000, SZ_4K);
// 读寄存器
u32 val = readl(dev->regs + 0x10);
}
static int my_remove(...)
{
iounmap(dev->regs); // 解除映射
}

-
✅ 您的理解完全正确 :
platform_device最终调用device的注册逻辑,platform_driver最终调用driver的注册逻辑 -
✅ 实现方式 :通过结构体嵌套(不是继承),将通用设备模型能力"组合"到具体设备类型中
-
✅ 核心价值 :Linux设备模型提供统一框架,各总线子系统只需定义自己的封装结构,实现代码复用 和统一管理
-
✅ 生命周期 :
platform_device和platform_driver的生命周期由内部的device和driver管理,通过container_of实现双向访问
四、实际是"资源"与"实体"的区别
cpp
evm_ 只适用于 probe 期间获取的"资源":
✅ 内存(kmalloc)
✅ IO 映射(ioremap)
✅ 中断(request_irq)
✅ GPIO(gpio_request)
✅ 驱动主动创建的设备(如 rtc_device)
不适用于"实体"本身:
❌ 传入的 platform_device(外部定义)
❌ platform_driver(模块级)
❌ i2c_client(由 i2c-core 管理)
❌ spi_device(由 spi-core 管理)
五、Linux 2.4 版本情况确认
结论 :Linux 2.4 完全没有 devm_ 函数。
- 引入时间 :
devm_机制在 Linux 2.6.32(2009年)才正式引入
六、linux驱动开发中如果一个驱动出现内存问题其它驱动都有影响吗还是说隔离的
