驱动注册的全景视角:从 `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


你可能感兴趣的:(驱动注册的全景视角:从 `module_init` 到 `/dev/xxx` 的创建之路)