linux driver ------ 字符设备驱动 之 “ 创建设备节点流程 ”
在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device_register()与device_add(),将device_create()函数展开不难发现:其实device_create()内调用了device_register(),device_register()内调用了device_add()。
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) { ...... dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); ...... return dev; }
struct device *device_create_vargs(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, va_list args) { ...... dev->devt = devt; dev->class = class; dev->parent = parent; dev->release = device_create_release; dev_set_drvdata(dev, drvdata); ...... retval = device_register(dev); ...... }
int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); }
加载驱动,执行device_add()函数,device_add()会在/sys目录对应设备目录下创建uevent属性节点,应用层的udev / mdev则会根据uevent来创建/dev目录下的设备节点,这里关于udev的部分不再赘述,我们继续分析device_create()、device_register()、device_add()三个函数在实际运用中的区别。 以一个简单的led设备字符设备驱动为例,下面分别用device_create()、device_register()、device_add()三个函数来创建设备节点“/dev/led”:
1. device_create()
static class *led_class; static int __init led_init(void) { int ret; dev_t devno; struct cdev *cdev; struct dev *dev; /* 注册设备号 */ ret = alloc_chrdev_region(&devno, 0, 1, "led"); if (ret < 0) return ret; /* 分配、初始化、注册cdev*/ cdev = cdev_alloc(); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); goto out_unregister_devno; } cdev_init(&cdev, &led_fops); cdev.owner = THIS_MODULE; ret = cdev_add(&cdev, devno, 1); if (ret) goto out_free_cdev; /* 创建设备类 */ led_class = class_create(THIS_MODULE, "led_class"); if (IS_ERR(led_class)) { ret = PTR_ERR(led_class); goto out_unregister_cdev; } /* 创建设备节点 */ dev = device_create(led_class, NULL, devno, NULL, "led"); if (IS_ERR(dev)) { ret = PTR_ERR(dev); goto out_del_class; } return 0; out_del_class: class_destroy(c78x_class); out_unregister_cdev: cdev_del(cdev); out_free_cdev: kfree(cdev); out_unregister_devno: unregister_chrdev_region(devno, 1); return ret; } module_init(led_init);
2. device_register()
static class *led_class; static int __init led_init(void) { ...... /* 注册设备号 */ ...... /* 分配、初始化、注册cdev*/ ...... /* 创建设备类 */ ...... /* 创建设备节点 */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto out_del_class; } dev->class = led_class; // 关联设备类 dev->parent = NULL; dev->devt = devno; // 关联设备号 dev_set_drvdata(dev, NULL); dev_set_name(dev, "led"); // 设置节点名字 dev->release = device_create_release; ret = device_register(dev); if (ret) goto out_put_dev; return 0; out_put_dev: put_device(dev); kree(dev); out_del_class: class_destroy(c78x_class); out_unregister_cdev: cdev_del(cdev); out_free_cdev: kfree(cdev); out_unregister_devno: unregister_chrdev_region(devno, 1); return ret; } module_init(led_init);
3. device_add()
static class *led_class; static int __init led_init(void) { ...... /* 注册设备号 */ ...... /* 分配、初始化、注册cdev*/ ...... /* 创建设备类 */ ...... /* 创建设备节点 */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto out_del_class; } dev->class = led_class; // 关联设备类 dev->parent = NULL; dev->devt = devno; // 关联设备号 dev_set_drvdata(dev, NULL); dev_set_name(dev, "led"); // 设置节点名字 dev->release = device_create_release; device_initialize(dev); ret = device_add(dev); if (ret) goto out_put_dev; return 0; out_put_dev: put_device(dev); kree(dev); out_del_class: class_destroy(c78x_class); out_unregister_cdev: cdev_del(cdev); out_free_cdev: kfree(cdev); out_unregister_devno: unregister_chrdev_region(devno, 1); return ret; } module_init(led_init);
标签:
linux driver
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)