Linux中device_create、device_add、device_register的区别

目录

前言:

一、源码分析

1、device_create()

2、device_register

3、device_add

二、device_create()、device_register()、device_add()使用中的区别

1、device_create()

2、device_register()

3、device_add()


前言:


        Linux设备驱动开发中,经常会用到device_create()函数创建设备节点,除了device_create()内核源码中也经常用device_register()和device_add()创建设备节点。

        本文主要讲解device_create()、device_register()、deivce_add()的区别。

一、源码分析


1、device_create()


device_create函数是我们写设备驱动经常用到的函数,它的功能是创建一个设备,并注册它到sysfs中,源码如下:

/* 源码路径:drivers/base/core.c */
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
{va_list vargs;struct device *dev;va_start(vargs, fmt);dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);va_end(vargs);return dev;
}

device_create() 最终调用 device_create_groups_vargs()函数,device_create_groups_vargs功能是创建一个device并且将它注册到sysfs中

device_create()
--->device_create_vargs()
--->--->device_create_groups_vargs()

device_create_groups_vargs()函数源码:

static __printf(6, 0) struct device *
device_create_groups_vargs(struct class *class, struct device *parent,dev_t devt, void *drvdata,const struct attribute_group **groups,const char *fmt, va_list args)
{struct device *dev = NULL;int retval = -ENODEV;if (class == NULL || IS_ERR(class))goto error;dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev) {retval = -ENOMEM;goto error;}device_initialize(dev);    //初始化devicedev->devt = devt;dev->class = class;dev->parent = parent;dev->groups = groups;dev->release = device_create_release;dev_set_drvdata(dev, drvdata);retval = kobject_set_name_vargs(&dev->kobj, fmt, args);    //设置device节点名字if (retval)goto error;retval = device_add(dev);if (retval)goto error;return dev;error:put_device(dev);return ERR_PTR(retval);
}

可以看到,device_create()是device_add()的封装,最终还是调用 device_add()

2、device_register


/* drivers/base/core.c */
int device_register(struct device *dev)
{device_initialize(dev);    //初始化devicereturn device_add(dev);
}

device_register()也是device_add()的封装。

3、device_add


device_add()会在/sys目录对应设备目录下创建uevent属性节点,应用层的udev则会根据uevent来创建/dev目录下的设备节点,这里关于udev的部分不再赘述

/* drivers/base/core.c */
int device_add(struct device *dev)
{struct device *parent;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL;struct kobject *glue_dir = NULL;dev = get_device(dev);if (!dev)goto done;if (!dev->p) {error = device_private_init(dev);if (error)goto done;}/** for statically allocated devices, which should all be converted* some day, we need to initialize the name. We prevent reading back* the name, and force the use of dev_name()*/if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name);dev->init_name = NULL;}/* subsystems can specify simple device enumeration */if (!dev_name(dev) && dev->bus && dev->bus->dev_name)dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);if (!dev_name(dev)) {error = -EINVAL;goto name_error;}pr_debug("device: '%s': %s\n", dev_name(dev), __func__);parent = get_device(dev->parent);kobj = get_device_parent(dev, parent);if (IS_ERR(kobj)) {error = PTR_ERR(kobj);goto parent_error;}if (kobj)dev->kobj.parent = kobj;/* use parent numa_node */if (parent && (dev_to_node(dev) == NUMA_NO_NODE))set_dev_node(dev, dev_to_node(parent));/* first, register with generic layer. *//* we require the name to be set before, and pass NULL */error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);if (error) {glue_dir = get_glue_dir(dev);goto Error;}/* notify platform of device entry */error = device_platform_notify(dev, KOBJ_ADD);if (error)goto platform_error;error = device_create_file(dev, &dev_attr_uevent);if (error)goto attrError;error = device_add_class_symlinks(dev);if (error)goto SymlinkError;error = device_add_attrs(dev);if (error)goto AttrsError;error = bus_add_device(dev);if (error)goto BusError;error = dpm_sysfs_add(dev);if (error)goto DPMError;device_pm_add(dev);if (MAJOR(dev->devt)) {error = device_create_file(dev, &dev_attr_dev);if (error)goto DevAttrError;error = device_create_sys_dev_entry(dev);if (error)goto SysEntryError;devtmpfs_create_node(dev);}/* Notify clients of device addition.  This call must come* after dpm_sysfs_add() and before kobject_uevent().*/if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_ADD);/** Check if any of the other devices (consumers) have been waiting for* this device (supplier) to be added so that they can create a device* link to it.** This needs to happen after device_pm_add() because device_link_add()* requires the supplier be registered before it's called.** But this also needs to happen before bus_probe_device() to make sure* waiting consumers can link to it before the driver is bound to the* device and the driver sync_state callback is called for this device.*/if (dev->fwnode && !dev->fwnode->dev) {dev->fwnode->dev = dev;fw_devlink_link_device(dev);}bus_probe_device(dev);if (parent)klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);if (dev->class) {mutex_lock(&dev->class->p->mutex);/* tie the class to the device */klist_add_tail(&dev->p->knode_class,&dev->class->p->klist_devices);/* notify any interfaces that the device is here */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->add_dev)class_intf->add_dev(dev, class_intf);mutex_unlock(&dev->class->p->mutex);}
done:put_device(dev);return error;SysEntryError:if (MAJOR(dev->devt))device_remove_file(dev, &dev_attr_dev);DevAttrError:device_pm_remove(dev);dpm_sysfs_remove(dev);DPMError:bus_remove_device(dev);BusError:device_remove_attrs(dev);AttrsError:device_remove_class_symlinks(dev);SymlinkError:device_remove_file(dev, &dev_attr_uevent);attrError:device_platform_notify(dev, KOBJ_REMOVE);
platform_error:kobject_uevent(&dev->kobj, KOBJ_REMOVE);glue_dir = get_glue_dir(dev);kobject_del(&dev->kobj);Error:cleanup_glue_dir(dev, glue_dir);
parent_error:put_device(parent);
name_error:kfree(dev->p);dev->p = NULL;goto done;
}

二、device_create()、device_register()、device_add()使用中的区别


分别用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;
}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;
}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;
}module_init(led_init);

 


最后看下 dev_set_name 函数源码,它调用kobject_set_name_vargs去设置device节点名字

/* drivers/base/core.c */
int dev_set_name(struct device *dev, const char *fmt, ...)
{va_list vargs;int err;va_start(vargs, fmt);err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);va_end(vargs);return err;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/453204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全面了解CAN总线协议

提及总线&#xff0c;总是让人联想到那些交错在一起的计算机电线。那么这些电线如何发挥功效呢&#xff1f;这还得配合总线协议的管理来使用。那么今天我们介绍的就是CAN总线协议。看看这个协议的含义和应用吧。 CAN总线协议基本概念 1. 报文 总线上的信息以不同格式的报文发…

工业以太网之战:EtherCAT是如何杀出重围的?

前言 EtherCAT 是一种开放的实时工业以太网协议&#xff0c;由德国倍福公司开发并在 2003 年 4 月的汉诺威工业博览会上首次亮相&#xff0c;目前由 EtherCAT 技术协会&#xff08;ETG&#xff09;进行维护和推广。经过 21 年的不断发展&#xff0c;EtherCAT 显示出极强的生命…

移动 Web核心笔记(二)

空间转换 空间&#xff1a;是从坐标轴角度定义的 X 、Y 和 Z 三条坐标轴构成了一个立体空间&#xff0c;Z 轴位置与视线方向相同。 空间转换也叫 3D转换 属性&#xff1a;transform 平移 /*单独设置 z轴效果不明显*/ transform: translate3d(x, y, z); transform: translateX(…

PostgreSQL学习笔记:PostgreSQL vs MySQL

PostgreSQL 和 MySQL 都是广泛使用的关系型数据库管理系统&#xff0c;它们有以下一些对比&#xff1a; 一、功能特性 1. 数据类型支持 PostgreSQL&#xff1a;支持丰富的数据类型&#xff0c;包括数组、JSON、JSONB、范围类型、几何类型等。对于复杂数据结构的存储和处理非…

多线程——单例模式

目录 前言 一、设计模式 二、饿汉模式 三、懒汉模式 1.单线程版 2.多线程版 结尾 前言 前面的几篇文章中介绍了多线程编程的基础知识&#xff0c;在本篇文章开始&#xff0c;就会利用前面的多线程编程知识来编写一些代码案例&#xff0c;从而使大家可以更好的理解运用多…

Cypress安装用命令安装

安装node 试一下&#xff0c;安装yarn 用命令安装Cypress 下面找个截图说&#xff1a;会给用给几个用例引导你怎么写测试脚本

阿里云 EMR Serverless Spark 版正式开启商业化

阿里云 EMR Serverless Spark 版已于2024年9月14日正式商业化售卖&#xff0c;本文将简要介绍 EMR Serverless Spark 的产品优势、应用场景、支持地域&#xff0c;及计费模式等。 EMR Serverless Spark 是一款云原生&#xff0c;专为大规模数据处理和分析而设计的全托管 Server…

基于JSP实习管理系统【附源码】

基于SSM的学生管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登录 5.1.2…

数字身份管理建设是传统社会向数字社会演进的核心关键

当前&#xff0c;新一轮科技革命和产业变革突飞猛进。科学技术尤其是以互联网、大数据、云计算、人工智能和区块链等为代表的数字技术正与社会交往、社会服务、社区建设、社会治理等领域不断渗透融合&#xff0c;社会正在由人与环境构成的物理关系总和向“万物数字化”和万物互…

重磅!望繁信科技与德勤中国签署战略合作协议

2022年&#xff0c;望繁信科技与德勤中国签署流程挖掘战略合作协议&#xff01;双方强强联合&#xff0c;在拓展流程优化市场、推动企业数智融合等领域展开深度合作&#xff0c;持续共建具有全球影响力的流程挖掘新生态。 根据协议内容&#xff0c;双方计划在未来三年内&#x…

软考攻略/超详细/系统集成项目管理工程师/基础知识分享18

6.5数据分析及应用 6.5.1 数据集成&#xff08;掌握&#xff09; 数据集成就是将驻留在不同数据源中的数据进行整合&#xff0c;向用户提供统一的数据视图&#xff0c;使得用户能以透明的方式访问数据。 WebServices技术是一个面向访问的分布式计算模型&#xff0c;它的本质是…

RabbitMQ 入门(六)SpringAMQP五种消息类型(Direct Exchange)

一、发布订阅-DirectExchange&#xff08;路由模式&#xff09; 在Fanout模式中&#xff0c;一条消息&#xff0c;会被所有订阅的队列都消费。但是&#xff0c;在某些场景下&#xff0c;我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。 Direct Exchan…

关键链项目管理是什么?它如何优化传统项目管理?

在项目管理的世界里&#xff0c;方法论千千万万&#xff0c;但真正能够提升项目效率和成功率的却并不多见。关键链项目管理&#xff08;Critical Chain Project Management, CCPM&#xff09;作为一种独特且高效的管理方式&#xff0c;正在被越来越多的企业所采用。相较于传统的…

NAND 数据恢复:使用 VNR 闪存数据恢复软件提取闪存转储中的块

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是专业 NAND 闪存数据恢复工具 VN…

linux下离线安装jq工具

故障现象&#xff1a; 当前使用的是CentOS7&#xff0c; 使用sudo yum install jq这个命令后&#xff0c;总是报错 Loaded plugins: fastestmirror, langpacks Determining fastest mirrors ... Cannot find a valid baseurl for repo: extras/7/x86_64 使用uname -a查看我当…

Yolov10训练的餐盘菜品目标检测软件(包含源码及数据集)

本文摘要 摘要&#xff1a;本文主要使用YOLOV10深度学习框架自训练了一个“餐盘菜品目标检测模型”&#xff0c;基于此模型使用PYQT5实现了一款界面软件用于功能演示。让您可以更好的了解和学习&#xff0c;该软件支持图片、视频以及摄像头进行目标检测&#xff0c;本系统所涉…

gitlab项目转移群组

1、背景 项目pa不再使用&#xff0c;只需要备份代码就行。将项目pa从A群组转移到B群组。 2、转移 在群组A项目pa中&#xff0c;设置-通用-高级-转移项目

Linux 线程概念及线程控制

1.线程与进程的关系 执行流&#xff08;Execution Flow&#xff09;通常指的是程序执行过程中的控制路径&#xff0c;它描述了程序从开始到结束的指令执行顺序。例如我们要有两个执行流来分别进行加法和减法的运算&#xff0c;我们可以通过使用 fork 函数来创建子进程&#xf…

全面了解 NGINX 的负载均衡算法

NGINX 提供多种负载均衡方法&#xff0c;以应对不同的流量分发需求。常用的算法包括&#xff1a;最少连接、最短时间、通用哈希、随机算法和 IP 哈希。这些负载均衡算法都通过独立指令来定义&#xff0c;每种算法都有其独特的应用场景。 以下负载均衡方法&#xff08;IP 哈希除…

置分辨率设置多显示器的时候提示, 某些设置由系统管理员进行管理

遇到的问题 设置分辨率设置多显示器的时候提示&#xff08;如下图所示&#xff09;&#xff1a; 某些设置由系统管理员进行管理 解决方法 先试试这个方法&#xff1a; https://answers.microsoft.com/zh-hans/windows/forum/all/%E6%9B%B4%E6%94%B9%E5%88%86%E8%BE%A8%E7%8…