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

设备与驱动解绑机制问题:解绑未等待字符设备fops完成引发Oops

解决设备解绑时未等待字符设备操作完成的问题

核心思路

通过原子引用计数跟踪字符设备的打开状态,结合等待队列让remove函数阻塞,直到所有字符设备的文件句柄都被释放,确保字符设备的release日志先于设备移除日志输出,彻底避免ioctl执行中解绑导致的Oops。

具体实现步骤

1. 扩展设备私有结构体

在你的设备结构体中新增引用计数、等待队列和移除标记,用于同步状态:

struct my_device {
    struct device *dev;
    struct cdev cdev;
    atomic_t open_count;       // 原子计数,跟踪当前打开的文件句柄数
    wait_queue_head_t wait_q;  // 等待队列,供remove函数阻塞等待
    bool removing;             // 标记设备是否处于移除流程中
    // 其他设备相关成员变量
};

2. 初始化同步成员

在probe函数中完成引用计数和等待队列的初始化:

static int my_probe(struct platform_device *pdev) {
    struct my_device *my_dev = devm_kzalloc(&pdev->dev, sizeof(*my_dev), GFP_KERNEL);
    // ... 其他设备初始化逻辑 ...

    atomic_set(&my_dev->open_count, 0);
    init_waitqueue_head(&my_dev->wait_q);
    my_dev->removing = false;

    // 注册字符设备等后续操作
    // ...
    return 0;
}

3. 改造file_operations的open/release函数

  • open函数:先检查设备是否正在移除,拒绝新的打开请求,同时增加引用计数:
static int my_open(struct inode *inode, struct file *file) {
    struct my_device *my_dev = container_of(inode->i_cdev, struct my_device, cdev);

    // 设备正在移除时,拒绝新的打开操作
    if (my_dev->removing) {
        return -ENODEV;
    }

    atomic_inc(&my_dev->open_count);
    file->private_data = my_dev;
    // ... 其他open阶段逻辑 ...
    return 0;
}
  • release函数:减少引用计数,若设备正在移除且最后一个句柄被释放,唤醒等待队列中的remove函数:
static int my_release(struct inode *inode, struct file *file) {
    struct my_device *my_dev = file->private_data;

    atomic_dec(&my_dev->open_count);
    // 设备移除中且所有句柄已释放,唤醒阻塞的remove函数
    if (my_dev->removing && atomic_read(&my_dev->open_count) == 0) {
        wake_up(&my_dev->wait_q);
    }
    // ... 其他release阶段逻辑 ...
    return 0;
}

4. 修改remove函数,阻塞等待句柄释放

在remove函数中先标记设备状态,再等待所有打开句柄释放,最后执行资源清理:

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

    // 标记设备进入移除流程,拒绝新的打开请求
    my_dev->removing = true;

    // 等待所有文件句柄关闭,超时时间可根据业务需求调整(这里设为5秒)
    if (!wait_event_timeout(my_dev->wait_q, 
                           atomic_read(&my_dev->open_count) == 0, 
                           msecs_to_jiffies(5000))) {
        dev_warn(&pdev->dev, "Warning: Timeout waiting for file handles to close\n");
        // 超时后可选择强制清理,但需确保不会触发新的Oops
    }

    // 此时所有句柄已释放,安全注销字符设备并清理资源
    cdev_del(&my_dev->cdev);
    // ... 其他设备资源清理操作 ...

    dev_info(&pdev->dev, "Device removed successfully\n");
    return 0;
}

关键原理说明

  • 原子变量open_count确保多线程环境下计数的正确性,避免竞态问题。
  • removing标记阻断新的打开请求,防止解绑过程中出现新的ioctl操作。
  • 等待队列让remove函数进入休眠状态,避免忙等占用CPU资源,直到所有句柄释放才继续执行。
  • 超时机制防止因异常场景(如进程挂死)导致remove函数永久阻塞。

额外注意事项

  • 若你的ioctl包含长时间运行的任务,需在任务循环中检查removing标记,一旦发现设备正在移除,立即终止任务并返回错误,避免等待超时。
  • 所有访问设备资源的逻辑(如ioctl中)都要先检查removing状态,防止访问已释放的内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 06:05:23