驱动注册的全景视角:从 `module_init` 到 `/dev/xxx` 的创建之路


📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry



🧩 驱动注册的全景视角:从 module_init/dev/xxx 的创建之路

在 Linux 驱动开发过程中,驱动的“注册”到底发生在哪?probe()register 谁先谁后?设备节点 /dev/xxx 又是如何自动出现在系统中的?这些问题看似零散,实则密切关联,构成了驱动模型的关键骨架。本文将带你完整梳理内核驱动注册的全过程,帮助构建清晰的驱动执行链路。


在这里插入图片描述

✅ 一、Linux 内核驱动结构中常见的函数有哪些?

Linux 内核中的驱动程序通常包含以下几个核心函数与接口:

函数 / 宏名作用描述
module_init()指定驱动模块加载时调用的初始化函数
module_exit()指定驱动卸载时调用的清理函数
xxx_driver.probe()设备匹配成功后被调用,用于资源申请与初始化
xxx_driver.remove()驱动卸载或设备移除时调用,用于资源释放
xxx_driver.register()驱动注册函数,注册到对应总线
register_chrdev() / cdev_add()注册字符设备,用于 /dev 节点创建
class_create() / device_create()创建 /dev 节点和 sysfs 接口

✅ 二、probe()register() 的调用顺序是怎样的?

这是很多人易混淆的问题,其实:

  • register 在前,probe 在后

解释如下:

  1. register_driver() 系列函数(如 platform_driver_register() 会将驱动挂接到内核的设备模型中。
  2. 注册成功后,内核会遍历系统中的设备(如 platform_device),判断是否与该驱动匹配(通过 name、of_match_table、id_table 等方式)。
  3. 一旦匹配成功,就会调用 probe() 函数,开始驱动与设备的绑定和初始化。

✅ 三、probe() 函数中是否包含 class_create() 等函数?

视情况而定。

  • 如果你是字符设备驱动开发者,你通常会probe() 函数中调用如下函数
my_class = class_create(THIS_MODULE, "mydev");
device_create(my_class, NULL, devt, NULL, "mydev%d", minor);
  • 这两个函数用于向用户空间导出设备节点,最终会在 /dev 中生成你期望的设备文件,如 /dev/mydev0

🔍 补充说明:

  • class_create() 会在 /sys/class/ 下生成一个类;
  • device_create() 会创建 /dev/xxx/sys/class/xxx/ 下的节点。

✅ 四、驱动注册的调用入口究竟在哪里?

驱动的注册函数通常会写在 module_init() 宏指向的函数中,例如:

static int __init my_driver_init(void)
{
    return platform_driver_register(&my_driver);
}
module_init(my_driver_init);

这个初始化函数会在模块被加载时执行。


✅ 五、module_init()module_exit() 的作用与执行时机?

宏定义描述
module_init(func)指定模块加载时执行的函数(比如注册驱动)
module_exit(func)指定模块卸载时执行的函数(比如释放资源)

这两个宏会在模块加载与卸载的过程中自动执行,实际是注册到了特殊的段中:

  • __initcall 用于 module_init。
  • __exitcall 用于 module_exit。

✅ 六、字符设备节点 /dev/xxx 是如何创建的?

这是许多驱动开发新手关心的问题,下面以字符设备为例讲解整个流程:

1. 分配设备号(或使用静态设备号)

alloc_chrdev_region(&devt, 0, 1, "mydev");

2. 初始化并添加 cdev 结构

cdev_init(&my_cdev, &fops);
cdev_add(&my_cdev, devt, 1);

3. 创建 class 和 device,用于 udev 创建设备节点

my_class = class_create(THIS_MODULE, "mydev");
device_create(my_class, NULL, devt, NULL, "mydev%d", 0);

4. 用户空间中的 udev 会根据 sysfs 下的信息自动在 /dev/ 中创建对应节点。


✅ 七、一个完整的示意流程图

module_init() ─────┬────────┐
                   ▼        ▼
      register_xxx_driver() │
                            │
             匹配到 device -> probe()
                                ├── register_chrdev() / cdev_add()
                                ├── class_create()
                                └── device_create() → 生成 /dev/xxx

🔚 总结归纳

问题点简明回答
驱动常见函数module_init, probe, register, class_create, device_create
probe 和 register 顺序register 先,probe 后
class_create 调用位置通常在 probe 函数中
注册函数调用位置写在 module_init 指定的初始化函数中
module_init 作用模块加载时调用注册代码
/dev/xxx 创建机制class_create + device_create + udev

🎯 结语:建立驱动模型全局视角

驱动开发不只是调试 probe(),而是理解从驱动注册、设备匹配、字符设备注册到用户空间节点自动创建的一整套机制。掌握这一流程后,你才能真正从“写驱动”跨越到“设计驱动”的层次。

📌 如果你正准备开发一个字符设备或平台设备驱动,不妨从上述结构入手,搭建你自己的驱动框架。



📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值