01-register_chrdev_region解析

1. 函数原型

  register_chrdev_region()原型如下:

原    型: int register_chrdev_region(dev_t from, unsigned count, const char *name)
功    能: 注册字符设备
@param1: 起始设备号
@param2: 设备数量
@param3: 设备名字
@return: 成功返回0,失败返回负数

2. 调用过程

2.1 register_chrdev_region

int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
	struct char_device_struct *cd;
	dev_t to = from + count;			// 计算分配设备号的最大值
	dev_t n, next;

	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);	// 计算下一个设备号的值,其中次设备号为0
		if (next > to)					// 如果下一个设备号的值大于分配设备号的最大值,将to赋值给next,for循环只会执行一次
			next = to;
		cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
		if (IS_ERR(cd))
			goto fail;
	}
	return 0;
fail:
	to = n;
	for (n = from; n < to; n = next) {
		next = MKDEV(MAJOR(n)+1, 0);
		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
	}
	return PTR_ERR(cd);
}

 在内核4.4版本中,MKDEV(ma,mi) ((ma)<<8 | (mi)),与之前的主设备号占高12位,次设备号占低20位不同,所以次设备号只能从0~255,一共有256个值。定义如下(linux-4.9.28\include\uapi\linux\kdev_t.h):

#define MAJOR(dev)	 ((dev)>>8)
#define MINOR(dev)	 ((dev) & 0xff)
#define MKDEV(ma,mi) ((ma)<<8 | (mi))

 简单绘制一个图表示两个相邻的设备号之间的关系。其中表格上面的红色字表示主设备号,表格中间的黑色数字表示每个主设备号下的次设备号。
在这里插入图片描述
 假设注册字符设备时,主设备定义的是5,起始次设备号为0,也就是 from = MKDEV(5, 0),数量是4,也就是 to = from + count = MKDEV(5, 4-1),在上图中用 to_1 表示,此时在图中可以看出 next > to,这是for循环只会执行一次,也就是 __register_chrdev_region 只会执行一次,而当一次注册的设备号 >256 的时候,例如注册260个时,此时to在图中的位置用 to_2 表示,这种情况下for循环会执行多次,相应的__register_chrdev_region 也会执行多次,本次只分析for循环执行一次的过程。
 判断完注册的设备数量是否会溢出之后,接着会调用 __register_chrdev_region,传入的参数依次是主设备号、次设备号、设备数量、设备名字。

 下面内容选自博客 http://blog.chinaunix.net/uid-29379418-id-4992151.html

2.2 __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 i;
    int ret = 0;

    cd = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL);/*slab分配一个char_device_struct变量*/
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);

    memset(cd, 0, sizeof(struct char_device_struct));/*将刚刚分配的变量的内存区清零*/

    write_lock_irq(&chrdevs_lock);/*关中断,禁止内核抢占+读写锁*/

    /* temporary */
    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;
        ret = major;
    }

    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;/*申请设备号的个数*/
    cd->name = name;
    /****************以上为第一部分,处理char_device_struct变量的分配和初始化************/
    /****************以下为第二部分,将char_device_struct变量注册到内核*****************/
    i = major_to_index(major);/*将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引*/

    /*
     *退出循环:
         (1)chrdevs[i]为空
         (2)chrdevs[i]的主设备号大于major
         (3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor
     */
	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
		if ((*cp)->major > major ||
		    ((*cp)->major == major &&
		     (((*cp)->baseminor >= baseminor) ||
		      ((*cp)->baseminor + (*cp)->minorct > baseminor))))
			break;
    /*
     *如果*cp不空,并且*cp的major与要申请的major相同,
     *此时,如果(*cp)->baseminor < baseminor + minorct,就会发生冲突
     *因为和已经分配了的设备号冲突了。
     *出错!
     */
	/* 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;
    write_unlock_irq(&chrdevs_lock);
    return cd;
out:
    write_unlock_irq(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}

2.3 char_device_struct 结构体

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];	// 255

总结:体上分为两个步骤:
 1. char_device_struct类型变量的分配以及初始化
 2. 将char_device_struct变量注册到内核

 1.char_device_struct类型变量的分配以及初始化
  (1) 首先,调用 kmalloc 分配一个 char_device_struct 变量cd。检查返回值,进行错误处理。
  (2) 将分配的char_device_struct变量的内存区清零memset。
  (3) 获取chrdevs_lock读写锁,并且关闭中断,禁止内核抢占,write_lock_irq。
  (4) 如果传入的主设备号major不为0,跳转到第(7)步。
  (5) 这时,major为0,首先需要分配一个合适的主设备号。将 i 赋值成 ARRAY_SIZE(chrdevs)-1,其中的 chrdevs 是包含有256个char_device_struct *类型的数组,然后递减 i 的值,直到在chrdevs数组中出现 NULL。当chrdevs数组中不存在空值的时候,ret = -EBUSY; goto out;
  (6) 到达这里,就表明主设备号major已经有合法的值了,接着进行char_device_struct变量的初始化。设置major, baseminor, minorct以及name。
 2. 将char_device_struct变量注册到内核
  (7) 将 i 赋值成 major_to_index(major) 将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引
  (8) 进入循环,在chrdevs[i]的链表中找到一个合适位置。
   退出循环的条件:
   (1)chrdevs[i]为空。
   (2)chrdevs[i]的主设备号大于major。
   (3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor。
   注意:cp = &(*cp)->next,cp是char_device_struct **类型,(*cp)->next是一个char_device_struct 类型,所以&(cp)->next,就得到一个char_device_struct **,并且这时候由于是指针,所以对cp赋值,就相当于对链表中的元素的next字段进行操作。
  (9) 进行冲突检查,因为退出循环的情况可能造成设备号冲突(产生交集)。如果
cp不空,并且
cp的major与要申请的major相同,此时,如果(*cp)->baseminor < baseminor + minorct,就会发生冲突,因为和已经分配了的设备号冲突了。出错就跳转到ret = -EBUSY; goto out;
  (10) 到这里,内核可以满足设备号的申请,将cd链接到链表中。
  (11) 释放chrdevs_lock读写锁,开中断,开内核抢占。
  (12) 返回加入链表的char_device_struct变量cd。
  (13) out出错退出
   a. 释放chrdevs_lock读写锁,开中断,开内核抢占。
   b. 释放char_device_struct变量cd,kfree。
   c. 返回错误信息

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值