device_add()浅析

本文介绍了Linux设备驱动模型中设备注册和注销的过程,包括device_add()和device_unregister()函数的实现细节,展示了如何通过引用计数管理和设备生命周期。
/*
    添加一个设备到liux设备驱动模型中
    device_register()
    {
        device_initialize();
        device_add();
    }
*/
int device_add(struct device * dev)
{
    struct device * parent = NULL;
    struct class_interface * class_intf;
    int error = -EINVAL;

    dev = get_device(dev);
    /**
        增加设备的引用计数
        过程:
            to_dev(kobject_get(&dev->kobj));

            struct kobject *kobject_get(struct kobject *kobj)
            {
                if (kobj)
                {
                    kref_get(&kobj->kref)
                    {
                    //struct kref :引用计数器,嵌套在kobject中记录该kobejct的引用计数,包含一个atomic_t类型的计数值
                    //struct kref {
                    //    atomic_t refcount;//当它的值为0的时候,kobject的生命周期就结束了,release会被调用
                    //};
                        atomic_inc(&kref->refcount); 
                    }
                }
                return kobj;
            }   
    /**
        再设备的卸载时,也会减少引用计数,而且如果引用计数为0,调用release函数
    */  
    void device_unregister(struct device *dev)
    {
        pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
        device_del(dev);
        put_device(dev)
        {
            if (dev)
            {
                kobject_put(&dev->kobj)
                {
                    kref_put(&kobj->kref, kobject_release)
                    {
                        //从原子变量refcount中减去 1 
                        if (atomic_dec_and_test(&kref->refcount)) 
                        {
                            release(kref);
                            return 1;
                        }
                    }
                }
            }
        }
    }
    if (!dev)
    {
        goto done;              
    }   
    */


在 Linux 驱动开发中,字符设备的注册通常通过 `cdev_add` 函数完成,而非 `cdev_device_add`。该函数负责将字符设备结构体 `struct cdev` 添加到内核的字符设备映射表中,并将其与设备号绑定,使得设备可以被访问。`cdev_add` 是内核中用于注册字符设备的标准接口,其行为在内核源码中具有明确的实现逻辑。 字符设备的添加流程通常包括以下几个步骤: 1. 初始化字符设备结构体,使用 `cdev_init` 函数将 `struct cdev` 与文件操作函数集合 `file_operations` 进行绑定。 2. 设置设备号,使用 `MKDEV` 或从设备号分配器中获取。 3. 调用 `cdev_add` 函数将字符设备注册到内核中。 4. 使用 `class_create` 和 `device_create` 创建设备类和设备节点,以便用户空间可以通过 `/dev` 目录访问设备[^2]。 示例代码如下: ```c struct cdev my_cdev; dev_t dev_num; // 分配设备号 alloc_chrdev_region(&dev_num, 0, 1, "my_device"); // 初始化字符设备 cdev_init(&my_cdev, &my_fops); my_cdev.owner = THIS_MODULE; // 添加字符设备到内核 if (cdev_add(&my_cdev, dev_num, 1)) { printk(KERN_ERR "Failed to add cdev\n"); unregister_chrdev_region(dev_num, 1); } ``` 在 `cdev_add` 的实现中,主要完成以下操作: - 将设备号与字符设备结构体进行映射。 - 调用 `kobj_map` 函数将字符设备注册到全局的字符设备映射表 `cdev_map` 中。 - 增加字符设备父对象的引用计数,以确保其不会在设备使用期间被释放[^3]。 需要注意的是,`cdev_add` 仅完成字符设备在内核中的注册,不会在 `/sys` 和 `/dev` 中创建设备节点。为了在用户空间创建设备节点,通常还需要调用 `class_create` 和 `device_create`,这些函数会触发 udev 机制自动生成设备节点。 ### 示例:完整字符设备注册流程 ```c struct class *my_class; struct device *my_device; // 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { printk(KERN_ERR "Failed to create class\n"); cdev_del(&my_cdev); unregister_chrdev_region(dev_num, 1); return PTR_ERR(my_class); } // 创建设备节点 my_device = device_create(my_class, NULL, dev_num, NULL, "my_device"); if (IS_ERR(my_device)) { printk(KERN_ERR "Failed to create device\n"); class_destroy(my_class); cdev_del(&my_cdev); unregister_chrdev_region(dev_num, 1); } ``` 上述代码展示了如何结合 `cdev_add` 和设备类接口,完成字符设备在内核和用户空间的完整注册流程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值