94 从源码看如何管理(分配)设备号

一、chrdevs哈希表

用于管理设备号
定义在 ebf-buster-linux/fs/char_dev.c

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 		fs.h

示意图:
在这里插入图片描述
注意上图中的一个节点可以表示 一个主设备号,一段次设备号

二、、 __register_chrdev_region 相关函数分析

此函数用于向 linux kernel 注册一个主设备号,一个或多个次设备号
定义在 char_dev.c

/* 参数含义依次为:
 * 主设备号,次设备号起始,次设备号数量,设备名字
 */
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);

	/* 若主设备号为0,由kernel分配一个设备号 */
	if (major == 0) {
		/* 从哈希表中找一个空闲位置 */
		for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
			if (chrdevs[i] == NULL)
				break;
		}

		if (i == 0) {
			ret = -EBUSY;
			goto out;
		}
		major = i;
	}
	// 保存传进来的四个参数
	cd->major = major; /* 主设备号 */
	cd->baseminor = baseminor; /* 次设备号起始 */
	cd->minorct = minorct; /* 次设备号数量 */
	strlcpy(cd->name, name, sizeof(cd->name));
	/* 计算在哈希表中的位置,就是对 255 进行取余 */
	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;

	/* 
	 * 主设备号相同的情况下需要判断次设备号是否冲突  
	 * 里面两个 if 判断
	 */
	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;
		}
	}
	/* 链表的节点插入操作 */
	cd->next = *cp;
	*cp = cd;
	mutex_unlock(&chrdevs_lock);
	return cd;
out:
	mutex_unlock(&chrdevs_lock);
	kfree(cd);
	return ERR_PTR(ret);
}

保存 新注册的设备号chrdevs哈希表 中,防止设备号冲突

分析结论:

哈希表中每个元素都是一个链表。
链表中的每一个元素似乎可以表示一个主设备号,一段次设备号(多个设备)

相关的其他函数

int register_chrdev_region(dev_t from, unsigned count, const char *name);
	此函数内部会调用 __register_chrdev_region。
	申请自己指定的一段设备号。这段设备号可能会包含多个主设备号。
	而 __register_chrdev_region 每次调用只能申请一个主设备号,一个或多个次设备号。
	因此在此函数内部要进行判断,分次调用 __register_chrdev_region。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
							const char *name);
	由系统分配一个设备号,并调用 __register_chrdev_region 进行注册。

static struct char_device_struct * 
	__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct);
	此函数用于注销一段设备号
	在哈希表 chrdevs 中找到对应的节点,删除之。
	由于之前是kzalloc动态申请,此时不需要手动free了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值