Linux file->private

一、存共享硬件对象

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_deviceplatform_driver 的生命周期由内部的 devicedriver 管理,通过 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驱动开发中如果一个驱动出现内存问题其它驱动都有影响吗还是说隔离的

相关推荐
WongKyunban1 小时前
在Linux下制作软件安装包
linux·运维·服务器
IT 乔峰1 小时前
Centos7中部署DNS服务器
linux
hweiyu001 小时前
Linux 命令:parted
linux·运维·服务器
烽火聊员1 小时前
CertificateCreator生成服务器证书server.pfx
运维·服务器
搞全栈小苏2 小时前
Linux下 cmake所有版本适用/下载编译使用教程 附cmake3.26安装包
linux·运维·ubuntu
MyFreeIT2 小时前
Docker Manual
运维·docker·容器
菜鸟冲锋号2 小时前
问题:增量关联(实时同步新数据) 这个场景中,如果hudi_pay 变更了一条数据,hudi_order_pay_join 结果的数据会跟着变化吗
服务器·前端·数据库
F***E2392 小时前
Nginx实现接口复制
运维·nginx·junit
wanhengidc2 小时前
使用云手机都要注意哪些?
运维·服务器·科技·游戏·智能手机