推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
更多学习视频请关注 B 站:嵌入式Jerry
module_init
到 /dev/xxx
的创建之路在 Linux 驱动开发过程中,驱动的“注册”到底发生在哪?probe()
和 register
谁先谁后?设备节点 /dev/xxx
又是如何自动出现在系统中的?这些问题看似零散,实则密切关联,构成了驱动模型的关键骨架。本文将带你完整梳理内核驱动注册的全过程,帮助构建清晰的驱动执行链路。
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
在后。register_driver()
系列函数(如 platform_driver_register()
) 会将驱动挂接到内核的设备模型中。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
是如何创建的?这是许多驱动开发新手关心的问题,下面以字符设备为例讲解整个流程:
alloc_chrdev_region(&devt, 0, 1, "mydev");
cdev_init(&my_cdev, &fops);
cdev_add(&my_cdev, devt, 1);
my_class = class_create(THIS_MODULE, "mydev");
device_create(my_class, NULL, devt, NULL, "mydev%d", 0);
/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