第三章:字符设备驱动-7:Char Device Registration

In continuation of the previous text 第三章:字符设备驱动-6:The inode Structure, let's GO ahead.

Char Device Registration

As we mentioned, the kernel uses structures of type struct cdev to represent char devices internally. Before the kernel invokes your device’s operations, you must allocate and register one or more of these structures.* To do so, your code should include <linux/cdev.h>, where the structure and its associated helper functions are defined.

There are two ways of allocating and initializing one of these structures. If you wish to obtain a standalone cdev structure at runtime, you may do so with code such as:

如前所述,内核使用 struct cdev 类型的结构在内部表示字符设备。在内核调用你的设备操作之前,你必须分配并注册一个或多个这样的结构。要完成此操作,你的代码需要包含 <linux/cdev.h> 头文件,该结构及其相关辅助函数均在此定义。

有两种分配和初始化 struct cdev 结构的方式:

如果你希望在运行时获取一个独立的 cdev 结构,可以使用如下代码:

struct cdev *my_cdev = cdev_alloc( );
my_cdev->ops = &my_fops;

Chances are, however, that you will want to embed the cdev structure within a device-specific structure of your own; that is what scull does. In that case, you should initialize the structure that you have already allocated with:

不过,更常见的做法是将 cdev 结构嵌入到你自己定义的设备特定结构中scull 驱动就是这样做的)。在这种情况下,你应该使用以下函数初始化已分配的结构:

void cdev_init(struct cdev *cdev, struct file_operations *fops);

Either way, there is one other struct cdev field that you need to initialize. Like the
file_operations structure, struct cdev has an owner field that should be set to THIS_MODULE.
Once the cdev structure is set up, the final step is to tell the kernel about it with a call to:

无论哪种初始化方式,struct cdev 都有一个字段需要你手动初始化:和 file_operations 结构一样,struct cdev 也有一个 owner 字段,该字段应设置为 THIS_MODULE

一旦 cdev 结构设置完毕,最后一步是通过以下调用告知内核该设备的存在:

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

Here, dev is the cdev structure, num is the first device number to which this device responds, and count is the number of device numbers that should be associated with the device. Often count is one, but there are situations where it makes sense to have more than one device number correspond to a specific device. Consider, for example, the SCSI tape driver, which allows user space to select operating modes (such as density) by assigning multiple minor numbers to each physical device.

其中:

  • dev 是 cdev 结构指针;

  • num 是该设备对应的起始设备号;

  • count 是与该设备关联的连续设备号数量。

通常 count 设为 1,但某些场景下多个设备号对应一个特定设备会更合理。例如,SCSI 磁带驱动允许用户空间通过为每个物理设备分配多个次设备号来选择操作模式(如密度)。

There are a couple of important things to keep in mind when using cdev_add. The first is that this call can fail. If it returns a negative error code, your device has not been added to the system. It almost always succeeds, however, and that brings up the other point: as soon as cdev_add returns, your device is “live” and its operations can be called by the kernel. You should not call cdev_add until your driver is completely ready to handle operations on the device.
To remove a char device from the system, call:

使用 cdev_add 时有几个重要注意事项:

  1. 可能失败:该函数可能返回负的错误码,表明设备未成功添加到系统中(尽管这种情况很少见);

  2. 设备立即激活:一旦 cdev_add 返回成功,你的设备就会 “上线”,内核可能立即调用其操作方法。因此,必须在驱动完全准备好处理设备操作后,再调用 cdev_add

要从系统中移除字符设备,需调用:

void cdev_del(struct cdev *dev);

Clearly, you should not access the cdev structure after passing it to cdev_del.

显然,将 cdev 结构传递给 cdev_del 后,就不应再访问该结构了。

补充说明:

  1. cdev_add 的错误处理 

    尽管 cdev_add 失败概率低,但驱动仍需处理错误(如设备号冲突),典型做法是在初始化失败时回滚已分配的资源:

    int ret;
    // 分配设备号
    ret = alloc_chrdev_region(&dev_num, 0, 1, "scull");
    if (ret < 0)
        return ret;
    // 初始化并添加 cdev
    cdev_init(&scull_dev.cdev, &scull_fops);
    scull_dev.cdev.owner = THIS_MODULE;
    ret = cdev_add(&scull_dev.cdev, dev_num, 1);
    if (ret < 0) {
        unregister_chrdev_region(dev_num, 1);  // 回滚设备号
        return ret;
    }
    

  2. count 参数的实际意义

    count 表示设备可响应的设备号范围(从 num 开始的 count 个连续编号)。例如,若 num=MKDEV(10, 0) 且 count=5,则设备会处理次设备号 0-4 的请求。这种设计适用于 “一个驱动管理多个同类设备” 的场景(如同一型号的 8 个串口,共享主设备号,次设备号 0-7)。

  3. cdev_del 与资源释放的顺序

    模块卸载时,需按 “先注销设备,再释放设备号” 的顺序操作,避免内核访问已释放的资源;

  4. cdev 结构的生命周期管理

    • 若通过 cdev_alloc() 动态分配:cdev_del 会自动释放该结构(无需手动 kfree);

    • 若嵌入自定义结构(静态分配或 kmalloc 分配):cdev_del 仅注销设备,不释放内存,需由驱动自行管理自定义结构的生命周期。


技术交流,欢迎加入社区:GPUers

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值