You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于正确使用sysfs_notify_dirent()的示例及文档求助

正确使用sysfs_notify_dirent()实现sysfs属性的poll支持(原子上下文场景)

没问题,我正好有过类似的驱动开发经验,给你拆解一下sysfs_notify_dirent()的正确用法,结合你的ISR场景给个完整示例:

首先得明确核心原因:sysfs_notify()需要先获取目标属性所在目录的struct dentry,过程中可能涉及睡眠操作,绝对不能在ISR这种原子上下文里调用。而sysfs_notify_dirent()直接使用提前保存的struct sysfs_dirent指针,不需要睡眠,完美适配ISR场景。

下面是完整的代码示例,一步步对应你的需求:

1. 定义设备结构体,保存关键指针

我们需要提前保存sysfs属性对应的sysfs_dirent指针,以及适配原子上下文的同步机制:

#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/spinlock.h>
#include <linux/wait.h>

struct my_device {
    struct device dev;
    struct sysfs_dirent *attr_sd;  // 保存目标属性的sysfs_dirent指针
    spinlock_t data_lock;          // 保护数据的自旋锁(适配ISR上下文)
    wait_queue_head_t wait_queue;  // poll等待队列,用于挂起用户进程
    bool has_new_data;             // 标记是否有新数据待读取
};

2. 实现sysfs属性的show函数

这个函数负责向用户空间返回数据,同时重置新数据标志:

static ssize_t my_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct my_device *my_dev = container_of(dev, struct my_device, dev);
    ssize_t ret;

    // 自旋锁保护,避免ISR和用户空间的操作冲突
    spin_lock(&my_dev->data_lock);
    // 这里简化处理,实际替换成你的数据读取逻辑即可
    ret = sprintf(buf, "%d\n", my_dev->has_new_data ? 1 : 0);
    my_dev->has_new_data = false;  // 读取完成后重置标志
    spin_unlock(&my_dev->data_lock);

    return ret;
}

// 定义只读属性,权限设为用户可读
static DEVICE_ATTR(my_attr, S_IRUGO, my_attr_show, NULL);

3. 实现poll函数

用户空间调用poll()/epoll()时会触发这个函数,我们需要检查数据状态并管理等待队列:

static unsigned int my_attr_poll(struct file *filp, poll_table *wait)
{
    struct my_device *my_dev = container_of(filp->private_data, struct my_device, dev);
    unsigned int mask = 0;

    // 将当前进程添加到等待队列,没有数据时会休眠
    poll_wait(filp, &my_dev->wait_queue, wait);

    spin_lock(&my_dev->data_lock);
    if (my_dev->has_new_data) {
        // 有新数据,返回POLLIN让用户空间知道可读
        mask |= POLLIN | POLLRDNORM;
    }
    spin_unlock(&my_dev->data_lock);

    return mask;
}

4. 设备初始化:创建属性并保存sysfs_dirent

在probe函数(进程上下文,允许睡眠)中创建sysfs属性,并获取对应的sysfs_dirent指针:

static int my_device_probe(struct platform_device *pdev)
{
    struct my_device *my_dev;
    int ret;

    my_dev = devm_kzalloc(&pdev->dev, sizeof(*my_dev), GFP_KERNEL);
    if (!my_dev)
        return -ENOMEM;

    // 初始化同步机制
    spin_lock_init(&my_dev->data_lock);
    init_waitqueue_head(&my_dev->wait_queue);
    my_dev->has_new_data = false;

    // 注册设备(这里假设是platform设备,根据你的实际设备类型调整)
    my_dev->dev.parent = &pdev->dev;
    ret = devm_device_add(&pdev->dev, &my_dev->dev);
    if (ret)
        return ret;

    // 创建sysfs属性
    ret = sysfs_create_file(&my_dev->dev.kobj, &dev_attr_my_attr.attr);
    if (ret)
        return ret;

    // 获取属性对应的sysfs_dirent指针,必须在可睡眠上下文调用
    my_dev->attr_sd = sysfs_get_dirent(my_dev->dev.kobj.sd, dev_attr_my_attr.attr.name);
    if (!my_dev->attr_sd) {
        sysfs_remove_file(&my_dev->dev.kobj, &dev_attr_my_attr.attr);
        return -ENOENT;
    }

    // 注册IRQ(替换成你的实际中断号和触发方式)
    ret = devm_request_irq(&pdev->dev, YOUR_IRQ_NUMBER, my_device_isr,
                           IRQF_TRIGGER_RISING, "my_device_irq", my_dev);
    if (ret) {
        sysfs_put_dirent(my_dev->attr_sd);
        sysfs_remove_file(&my_dev->dev.kobj, &dev_attr_my_attr.attr);
        return ret;
    }

    platform_set_drvdata(pdev, my_dev);
    return 0;
}

5. ISR中调用sysfs_notify_dirent()

当ISR检测到新数据时,标记状态并通知用户空间:

static irqreturn_t my_device_isr(int irq, void *dev_id)
{
    struct my_device *my_dev = dev_id;

    // 标记有新数据,自旋锁保护避免并发问题
    spin_lock(&my_dev->data_lock);
    my_dev->has_new_data = true;
    spin_unlock(&my_dev->data_lock);

    // 关键操作:通知用户空间属性有更新,触发poll唤醒
    sysfs_notify_dirent(my_dev->attr_sd);

    return IRQ_HANDLED;
}

6. 设备移除:清理资源

一定要记得释放sysfs_dirent的引用,避免内存泄漏:

static int my_device_remove(struct platform_device *pdev)
{
    struct my_device *my_dev = platform_get_drvdata(pdev);

    // 释放sysfs_dirent的引用计数
    sysfs_put_dirent(my_dev->attr_sd);
    // 移除sysfs属性
    sysfs_remove_file(&my_dev->dev.kobj, &dev_attr_my_attr.attr);

    return 0;
}

关键注意事项

  • sysfs_get_dirent()必须在可睡眠上下文调用:比如probe函数,因为它内部可能会获取锁,不能在ISR里执行。
  • sysfs_notify_dirent()可以安全在原子上下文使用:它不需要分配内存或睡眠,只是唤醒等待该属性的poll进程。
  • 必须配对使用sysfs_get_dirent()和sysfs_put_dirent():避免sysfs_dirent的引用计数泄漏。
  • 数据同步必须用自旋锁:ISR和用户空间的show/poll函数都会访问has_new_data,自旋锁是原子上下文安全的同步方式。

内容的提问来源于stack exchange,提问作者Twoism

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:11:49