alloc_chrdev_region 分析

本文详细解析了Linux内核版本2.6.32中用于分配字符设备区域的alloc_chrdev_region函数的实现过程,包括其关键步骤和内部逻辑。

研究内核版本 2.6.32


首先,要查找函数 alloc_chrdev_region 的实现。

可以在源码树中使用find+grep查找

$ find . -name *.c | xargs grep -EHn 'alloc_chrdev_region\W*\(dev_t'
./fs/char_dev.c:231:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
找到实现,接下来就看看它吧。 羡慕


int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)
{
	struct char_device_struct *cd;
	cd = __register_chrdev_region(0, baseminor, count, name); 
	if (IS_ERR(cd))
		return PTR_ERR(cd);
	*dev = MKDEV(cd->major, cd->baseminor);
	return 0;
}

可以看到 关键是

__register_chrdev_region
这个函数在同一个文件中

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);

	/* temporary */
	if (major == 0) { // 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;
		ret = major;
	}

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

	i = major_to_index(major);
        
        // 检查 baseminor, minorct 是否合理
	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) // 可以看出chrdevs这是一个哈希表,解决冲突的方式是链表法
		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;
		}
	}

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


这篇文章中的哈希表画得不错


总结:

分析源码首先要找到它的实现在哪里(废话)。 着实现可以用 find+grep、 lxr、cscope等方法。见这里


### alloc_chrdev_region 的用法与实例 `alloc_chrdev_region` 是 Linux 内核中用于分配字符设备号的一个重要函数。它允许开发者请求一段未被使用的主次设备号区域,从而避免与其他设备冲突。 以下是 `alloc_chrdev_region` 的基本定义和参数说明: #### 参数描述 - **devid**: 返回的设备号指针,通常是一个 `dev_t` 类型变量地址。 - **baseminor**: 请求区间的起始次设备号。 - **count**: 请求连续设备号的数量。 - **name**: 字符串形式的名字,用来标识这个设备区间。 返回值为成功时的零或错误码[^2]。 下面展示了一个简单的代码示例,演示如何使用 `alloc_chrdev_region` 来分配设备号并初始化字符设备结构体。 ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> static dev_t first; /* 设备号 */ static struct cdev my_cdev; int init_module(void) { int ret; // 分配设备号 if ((ret = alloc_chrdev_region(&first, 0, 1, "my_char_device")) < 0) { printk(KERN_ERR "Failed to allocate char device region\n"); return ret; } // 初始化字符设备结构体 cdev_init(&my_cdev, &fops); my_cdev.owner = THIS_MODULE; // 向系统注册字符设备 if (cdev_add(&my_cdev, first, 1)) { unregister_chrdev_region(first, 1); // 注销设备号以防泄漏 printk(KERN_ERR "Error adding character device\n"); return -EFAULT; } printk(KERN_INFO "Allocated major number %d\n", MAJOR(first)); return 0; } void cleanup_module(void) { cdev_del(&my_cdev); // 删除字符设备 unregister_chrdev_region(first, 1); // 注销设备号 } ``` 上述代码展示了完整的流程:从调用 `alloc_chrdev_region` 获取设备号到最终注销设备号的过程。注意,在模块卸载时务必释放资源以防止内存泄露或其他副作用。 关于常见问题及其解决方案如下: - 如果遇到 `-EBUSY` 错误,则表示所选设备号已被占用;可以尝试更改基底次设备号或者增加计数以便寻找其他可用空间。 - 当发现无法加载模块可能是因为权限不足或者是内核版本不匹配引起的兼容性问题[^3]。 ### 总结 通过以上介绍可以看出,相比手动指定固定数值的方式而言,采用动态方法能够有效减少因重复配置而导致的功能失效风险。因此推荐优先考虑利用像 `alloc_chrdev_region` 这样的工具完成相应操作[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值