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

Linux字符设备操作失败及卸载后设备项残留问题求助

字符设备驱动问题分析与修正方案

问题1:设备节点操作报错的原因

  • 仅注册了主设备号,但未完成cdev结构体与设备号(主+次)的绑定及内核添加操作,导致内核无法将设备节点的操作映射到驱动的file_operations
  • 驱动的file_operations未正确赋值,或cdev_init调用时传入的指针错误
  • 未创建设备类与设备实例,内核设备模型中无该设备记录(即使手动mknod,也无法关联到驱动)

问题2:卸载模块后/proc/devices残留的原因

  • 模块退出函数未调用unregister_chrdev_region()释放已分配的设备号
  • 未调用cdev_del()删除已添加的cdev结构体,内核仍认为设备存在
  • 退出流程顺序错误,遗漏关键资源释放步骤

修正后的完整驱动代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define DEVICE_NAME "mycdev"
#define MINOR_COUNT 1  // 支持的次设备号数量

static int mycdev_major;
static struct cdev mycdev_cdev;
static struct class *mycdev_class;

// 设备打开操作
static int mycdev_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "mycdev opened\n");
    return 0;
}

// 设备释放操作
static int mycdev_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "mycdev released\n");
    return 0;
}

// 设备操作集合
static const struct file_operations mycdev_fops = {
    .owner = THIS_MODULE,
    .open = mycdev_open,
    .release = mycdev_release,
};

static int __init mycdev_init(void)
{
    int ret;
    dev_t dev_num;

    // 动态分配设备号(优先推荐,避免静态分配冲突)
    ret = alloc_chrdev_region(&dev_num, 0, MINOR_COUNT, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "Failed to allocate chrdev region\n");
        return ret;
    }
    mycdev_major = MAJOR(dev_num);
    printk(KERN_INFO "Allocated major number: %d\n", mycdev_major);

    // 初始化cdev并绑定操作集合
    cdev_init(&mycdev_cdev, &mycdev_fops);
    mycdev_cdev.owner = THIS_MODULE;

    // 将cdev添加到内核,建立设备号与驱动的关联
    ret = cdev_add(&mycdev_cdev, dev_num, MINOR_COUNT);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add cdev\n");
        unregister_chrdev_region(dev_num, MINOR_COUNT);
        return ret;
    }

    // 创建设备类,让udev自动生成设备节点
    mycdev_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(mycdev_class)) {
        printk(KERN_ERR "Failed to create class\n");
        cdev_del(&mycdev_cdev);
        unregister_chrdev_region(dev_num, MINOR_COUNT);
        return PTR_ERR(mycdev_class);
    }

    // 创建设备实例,生成/dev/mycdev1节点
    device_create(mycdev_class, NULL, dev_num, NULL, "%s1", DEVICE_NAME);

    printk(KERN_INFO "mycdev driver loaded\n");
    return 0;
}

static void __exit mycdev_exit(void)
{
    // 按顺序释放资源:设备实例→设备类→cdev→设备号
    device_destroy(mycdev_class, MKDEV(mycdev_major, 0));
    class_destroy(mycdev_class);
    cdev_del(&mycdev_cdev);
    unregister_chrdev_region(MKDEV(mycdev_major, 0), MINOR_COUNT);

    printk(KERN_INFO "mycdev driver unloaded\n");
}

module_init(mycdev_init);
module_exit(mycdev_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My Character Device Driver");
MODULE_AUTHOR("Your Name");

关键修正要点

  1. 设备号管理:
    • 用alloc_chrdev_region()动态分配设备号,避免静态分配的冲突问题
    • 退出时必须调用unregister_chrdev_region()释放设备号,确保/proc/devices清理干净
  2. cdev关联:
    • 通过cdev_init()绑定file_operations,cdev_add()将设备号与驱动关联,让内核能识别设备节点的操作请求
    • 退出时调用cdev_del()删除cdev,告知内核设备已移除
  3. 设备模型集成:
    • 创建设备类和实例,让udev自动生成设备节点,无需手动mknod(若坚持手动操作,需确保主/次设备号与驱动分配的一致)
  4. 错误回滚:
    • 每一步操作失败后,回滚已分配的资源,避免内存泄漏或残留注册信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:15:16