【linux API 分析】register_chrdev

news2025/1/20 5:48:08

linux kernel:4.19
在注册字符设备的时候,可使用register_chrdev()函数,其对应的注销函数是unregister_chrdev(),其定义是在include\linux\fs.h文件

register_chrdev()

首先分析register_chrdev()函数
其定义如下

static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)
{
	return __register_chrdev(major, 0, 256, name, fops);
}

该函数会调用__register_chrdev()函数,其定义位于fs\char_dev.c文件

/**
 * __register_chrdev() - create and register a cdev occupying a range of minors
 * @major: major device number or 0 for dynamic allocation
 * @baseminor: first of the requested range of minor numbers
 * @count: the number of minor numbers required
 * @name: name of this range of devices
 * @fops: file operations associated with this devices
 *
 * If @major == 0 this functions will dynamically allocate a major and return
 * its number.
 *
 * If @major > 0 this function will attempt to reserve a device with the given
 * major number and will return zero on success.
 *
 * Returns a -ve errno on failure.
 *
 * The name of this device has nothing to do with the name of the device in
 * /dev. It only helps to keep track of the different owners of devices. If
 * your module name has only one type of devices it's ok to use e.g. the name
 * of the module here.
 */
int __register_chrdev(unsigned int major, unsigned int baseminor,
		      unsigned int count, const char *name,
		      const struct file_operations *fops)
{
	struct char_device_struct *cd;
	struct cdev *cdev;
	int err = -ENOMEM;

	cd = __register_chrdev_region(major, baseminor, count, name);
	if (IS_ERR(cd))
		return PTR_ERR(cd);

	cdev = cdev_alloc();
	if (!cdev)
		goto out2;

	cdev->owner = fops->owner;
	cdev->ops = fops;
	kobject_set_name(&cdev->kobj, "%s", name);

	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
	if (err)
		goto out;

	cd->cdev = cdev;

	return major ? 0 : cd->major;
out:
	kobject_put(&cdev->kobj);
out2:
	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
	return err;
}

__register_chrdev()函数首先调用__register_chrdev_region()函数

/*
 * Register a single major with a specified minor range.
 *
 * If major == 0 this functions will dynamically allocate a major and return
 * its number.
 *
 * If major > 0 this function will attempt to reserve the passed range of
 * minors and will return zero on success.
 *
 * Returns a -ve errno on failure.
 */
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
			   int minorct, const char *name)
{
	struct char_device_struct *cd, **cp;
	int ret = 0;
	int i;

	cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
	if (cd == NULL)
		return ERR_PTR(-ENOMEM);

	mutex_lock(&chrdevs_lock);

	if (major == 0) {
		ret = find_dynamic_major();
		if (ret < 0) {
			pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
			       name);
			goto out;
		}
		major = ret;
	}

	if (major >= CHRDEV_MAJOR_MAX) {
		pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
		       name, major, CHRDEV_MAJOR_MAX-1);
		ret = -EINVAL;
		goto out;
	}

	cd->major = major;
	cd->baseminor = baseminor;
	cd->minorct = minorct;
	strlcpy(cd->name, name, sizeof(cd->name));

	i = major_to_index(major);

	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
		if ((*cp)->major > major ||
		    ((*cp)->major == major &&
		     (((*cp)->baseminor >= baseminor) ||
		      ((*cp)->baseminor + (*cp)->minorct > baseminor))))
			break;

	/* Check for overlapping minor ranges.  */
	if (*cp && (*cp)->major == major) {
		int old_min = (*cp)->baseminor;
		int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
		int new_min = baseminor;
		int new_max = baseminor + minorct - 1;

		/* New driver overlaps from the left.  */
		if (new_max >= old_min && new_max <= old_max) {
			ret = -EBUSY;
			goto out;
		}

		/* New driver overlaps from the right.  */
		if (new_min <= old_max && new_min >= old_min) {
			ret = -EBUSY;
			goto out;
		}

		if (new_min < old_min && new_max > old_max) {
			ret = -EBUSY;
			goto out;
		}

	}

	cd->next = *cp;
	*cp = cd;
	mutex_unlock(&chrdevs_lock);
	return cd;
out:
	mutex_unlock(&chrdevs_lock);
	kfree(cd);
	return ERR_PTR(ret);
}

其次调用cdev_alloc()函数

struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;
}

然后调用cdev_add()函数

/**
 * cdev_add() - add a char device to the system
 * @p: the cdev structure for the device
 * @dev: the first device number for which this device is responsible
 * @count: the number of consecutive minor numbers corresponding to this
 *         device
 *
 * cdev_add() adds the device represented by @p to the system, making it
 * live immediately.  A negative error code is returned on failure.
 */
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	int error;

	p->dev = dev;
	p->count = count;

	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;

	kobject_get(p->kobj.parent);

	return 0;
}

综上register_chrdev函数调用流程如下

register_chrdev()
	-->__register_chrdev()
		-->__register_chrdev_region()
		-->cdev_alloc()
		-->cdev_add()

从__register_chrdev_region()函数可以看出,该函数会根据传入的参数,去完善结构体 char_device_struct(除cdev结构体),并返回该结构体地址

static struct char_device_struct {
	struct char_device_struct *next;
	unsigned int major;
	unsigned int baseminor;
	int minorct;
	char name[64];
	struct cdev *cdev;		/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

该结构体中,存在一个链表,链表的作用分析如下:

#define CHRDEV_MAJOR_HASH_SIZE 255

可以看出分配的结构体指针数组有255个元素,由于有的主设备号大于254,故需要除以255取余,这样,当主设备号为256时,除以255取余为1,故存在数组chrdev[1]中,但chrdev也可存主设备号为1的元素,故使用链表,使主设备号为1的char_device_struct的next成员指向主设备号为256的char_device_struct

在这里插入图片描述

对于cdev结构体,其内容如下

struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
} __randomize_layout;

cdev_alloc()函数,该函数为cdev结构体分配内存空间,并初始化list以及kobj成员
对于kobj目前我没搞清楚,在这个时候看到了一篇文章,写得不错,有兴趣的去看这篇文章吧
链接: link

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SMAP(Soil Moisture Active and Passive)数据下载

SMAP&#xff08;Soil Moisture Active and Passive&#xff09;数据下载 打开网站先注册登录用户 然后打开SMAP下载的网站 点击HTTPS File System进入下载页面 然后点击HDF文件下载 下载之后在HDF View里面预览

Spring中反转控制与依赖注入

目录 一、反转控制&#xff08;IOC&#xff09; 二、依赖注入&#xff08;DI&#xff09; 一、反转控制&#xff08;IOC&#xff09; 在以前的学习中为成员变量的赋值&#xff0c;一般都是直接在代码中完成&#xff0c;但是这种方式会产生耦合。然而利用Spring对成员变量赋值可…

QT学习day2

一、思维导图 作业&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admi…

sublime怎么调中文?

Sublime Text是一个功能强大的文本编辑器&#xff0c;它被广泛使用于编码过程中。在开发过程中&#xff0c;Sublime Text界面的语言设置通常默认为英语&#xff0c;无法直接输入中文。那么如何调整Sublime Text编辑器的设置&#xff0c;以允许在界面中输入和编辑中文呢&#xf…

CSS盒子模型的详细解析

03-盒子模型 作用&#xff1a;布局网页&#xff0c;摆放盒子和内容。 盒子模型-组成 内容区域 – width & height 内边距 – padding&#xff08;出现在内容与盒子边缘之间&#xff09; 边框线 – border 外边距 – margin&#xff08;出现在盒子外面&#xff09; d…

计算机网络学习笔记(三):数据链路层(待更新)

目录 3.1 基本概念 3.1.1 数据链路和帧 3.1.2 三个基本问题 3.2 类型1&#xff1a;使用点对点信道的数据链路层&#xff08;路由器&#xff09; 3.2.1 点对点协议 PPP&#xff1a;特点 3.2.2 点对点协议 PPP&#xff1a;帧格式 3.2.3 点对点协议 PPP&#xff1a;工作状态 …

了解web基础,http协议

域名 例如&#xff1a;www.baidu.com dns域名&#xff1a; 网络上的通信都是基于ip&#xff0c;通信模式&#xff1a;TCP/IP Tcp建立连接和断开连接&#xff0c;都是要双方进行确认的 建立连接&#xff1a;三次握手 断开连接&#xff1a;四次挥手 通信时端到端 端口进行通…

ONLYOFFICE 文档 7.5 现已发布:新增 PDF 编辑器、屏幕朗读器等功能

我们已推出最新版的在线编辑器&#xff0c;整个套件具备多项增强功能。敬请继续阅读&#xff0c;了解所有更新。 全新 PDF 编辑器 PDF 是工作中很常见的文件格式。 我们经常需要打开各种PDF格式的宣传册&#xff0c;签署协议和合同等等。 我们一直记得这一点&#xff0c;一直…

NR SRS power control

这篇看下NR SRS power control的相关内容&#xff0c;主要内容集中在38.213 7.3章节&#xff0c;SRS power control与PUSCH很类似&#xff0c;当然细节上也有所不同&#xff0c;这里简单看下。 UL功率控制,主要是PUSCH/PUCCH/SRS/PRACH的传输功率。 对于所有PUSCH/PUCCH/SRS传输…

掌握.NET基础知识(一)

前言 本文将讲解一些.NET基础。NET基础是指在计算机编程中使用.NET框架所需要的基础知识..NET的认识: .NET是由微软开发的一个跨平台的应用程序开发框架。它包括一个运行时环境和一个面向对象的程序库&#xff0c;可以用于开发各种类型的应用程序&#xff0c;包括桌面应用程序、…

计算机操作系统-第十天

目录 1、操作系统的进程 进程的概念 进程的组成------PCB 进程的组成------程序段、数据段 旧知新学&#xff1a;《程序是如何运行的》 进程的特征 本节思维导图 1、操作系统的进程 进程的概念 当我们打开多个qq程序&#xff0c;我们会发现任务管理器的进程中有…

对代码感兴趣 但不擅长数学怎么办——《机器学习图解》来救你

目前&#xff0c;该领域中将理论与实践相结合、通俗易懂的著作较少。机器学习是人工智能的一部分&#xff0c;很多初学者往往把机器学习和深度学习作为人工智能入门的突破口&#xff0c;非科班出身的人士更是如此。当前&#xff0c;国内纵向复合型人才和横向复合型人才奇缺;具有…

C#中使用 ref

下面是一个示例&#xff0c;演示了如何在C#中使用 ref&#xff1a; class Program {static void Main(){int number 10;Console.WriteLine("原始值: " number);ModifyValue(ref number);Console.WriteLine("修改后的值: " number);}static void Modify…

关于罗克韦尔跟西门子PLC之间通讯的解决方案

不同品牌的PLC设备之间通讯一般是通过总线模块&#xff0c;或者直接在PLC内编程实现&#xff0c;这样不仅硬件成本高&#xff0c;而且开发调试的时间都很长&#xff0c;后期维护也相当麻烦。本文采用IGT-DSER智能网关模块&#xff0c;PLC内不用编程&#xff0c;通过PLC现有的以…

如何在 Python 中创建一个虚拟环境,以及为什么需要它

当用 Python 开发软件时&#xff0c;一个基本的方法是在你的机器上安装 Python&#xff0c;通过终端安装所有需要的库&#xff0c;在一个 .py 文件或笔记本中编写所有的代码&#xff0c;并在终端运行你的 Python 程序。 这是很多初学者和很多从使用 Python 进行数据分析过渡的…

【Note】CNN与现代卷积神经网络part4(附PyTorch代码)

文章目录 2.2 残差网络&#xff08;ResNet&#xff09;2.2.1 函数类2.2.2 残差块2.2.3 ResNet模型2.2.4 训练模型2.2.5 Summary 本《CNN与现代卷积神经网络》Note系列会共分为4个part&#xff0c;本文为part4。本Markdown共4k字。 2.2 残差网络&#xff08;ResNet&#xff09; …

文心一言 4.0 ERNIE-Bot 4.0 :ERNIE-Bot 4.0 大模型深度测试体验报告

本心、输入输出、结果 文章目录 文心一言 4.0 ERNIE-Bot 4.0 &#xff1a;ERNIE-Bot 4.0 大模型深度测试体验报告前言相关跳转文心一言 4.0 ERNIE-Bot 4.0 接口简介Bash 请求示例代码Windows 模式使用 Python 请求如果直接使用官方提供的代码文心一言 4.0 ERNIE-Bot 4.0 API 在…

GEE19:基于Landsat8的常见的植被指数逐年获取

植被指数逐年获取 1. 常见的植被指数1.1 比值植被指数&#xff08;Ratio vegetation index&#xff0c;RVI&#xff09;1.2 归一化植被指数&#xff08;Normalized Difference Vegetation Index&#xff0c;NDVI&#xff09;1.3 增强植被指数&#xff08;Enhanced Vegetation I…

nuxt3+ts:集成 百度-爱番番 客服

一、本文目标 nuxt3 ts 集成百度爱番番&#xff08;客服&#xff09; 百度爱番番—企业的一站式智能营销管家 二、百度爱番番客服能力 三、爱番番后台基础配置 3.1、设置中心 登录后点右上角设置中心 3.2、沟通与触达 -- > 站点设置 -- > 新建站点 填写信息 3.3、获…

jQuery的使用,下载以及一些小案例

24.jQuery的使用 1.官网地址 https://jquery.com/ 版本&#xff1a; ●1X:兼容E678等低版本浏览器&#xff0c;官网不再更新 ●2x:不兼容IE678等低版本浏览器&#xff0c;官网不再更新 ●3x:不兼容E678等低版本浏览器&#xff0c;是官方主要更新维护的版本 2 jQuery的使用步…