当前位置: 星创客 > 学习资源 > 讲师博文 > 设备号注册过程分析
设备号注册过程分析 时间:2017-11-08     来源:星创客

今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数:

 

l  register_chrdev_region();

 

l  alloc_chrdev_region();

 

设备号的数据类型是dev_t类型,是一个无符号长整型,在32位操作系统中,它的大小是4个字节,32位,高12位用来存放主设备号,低20位用来存放次设备号。

 

通过主设备号和次设备号合成设备号的宏如下:

 

MKDEV(major,minor);

 

从设备号中提取主设备号和次设备号的宏如下:

 

MAJOR(dev);

 

MINOR(dev);

 

接下来我们就来分析设备号注册过程:

 

一、自动分配设备号:
 

1.  调用内核提供的自动分配设备号函数,分配设备号 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)  
2. alloc_chrdev_region函数中继续调用如下函数,进行进一步申请 
struct char_device_struct *cd =  __register_chrdev_region(0, baseminor, count, name);		
/*在该函数中进行判断,如果major==0,执行if内的语句*/			
if (major == 0) {                     //自动分配时major == 0
/*
	*chrdevs是一个结构体指针数组,见附录1-1
*作用是从struct char_device_struct类型的结构体指针数组中找到一个空的结构体指针
*/
	for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {  // for(i=254;i>0;i--)
		if (chrdevs[i] == NULL)
		break;
	}
if (i == 0) {      //i == 0 ,说明,存放设备号的结构体指针数组已经用完
		ret = -EBUSY;
		goto out;
	}
major = i;         //否则,主设备号 = i
	ret = major;
cd->major = major;                       // 将主设备号赋值给cd->major
	cd->baseminor = baseminor;       // 将起始次设备号赋值cd->baseminor
	cd->minorct = minorct;    // 将传入的第三个参数赋值给程cd->minorct,
                                                     // 表示申请设备号的个数
	strlcpy(cd->name, name, sizeof(cd->name));  //设备名拷贝
/*
*struct char_device_struct *cd, **cp;
*cp存放的是结构体指针的地址,基于当前的条件,*cp == NULL ,所以for循环条件不成立
*/
--------------------------------------不执行-------------------------------------
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 == NULL ,所以条件不成立*/
---------------------------------------不执行-----------------------------------------
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;                  //cd ->next = NULL
*cp = cd; 			//*cp[chrdevs[i]] , cd 结构体在上面赋值的
mutex_unlock(&chrdevs_lock);
return cd;
回到alloc_chrdev_region函数的*dev = MKDEV(cd->major, cd->baseminor);
实际上就申请了一个主设备号,次设备号
二、用户指定设备号注册(eg:major = 250;count=3)
1. int register_chrdev_region(dev_t from, unsigned count, const char *name);
2. 在该函数中进行如下操作:
dev_t to = from + count;      // dev_t to =  MKDEV(dev_major,dev_minor) +3;
dev_t n, next;
/*如果申请的设备编号范围跨越了主设备号,
*它会把分配范围内的编号按主设备号分割
 *成较小的子范围,并在每个子范围上调用
 *__register_chrdev_region() 。如果其中
*有一次分配失败的话,那会把之前成功分配的都全部退回*/
// for(n = MKDEV(dev_major,dev_minor); n<to;n=next)循环一次(next = to)
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);         // next = MKDEV(250+1,0);
	if (next > to)                       // if(MKDEV(251,0)>MKDEV(250,0)+3) [成立]
	next = to;                          // next = MKDEV(250,0)+3
cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name);
				//__register_chrdev_region(250, 0,3, "xxx_demo");
	       {
 cd->major = major;           //cd->major = 250;
			cd->baseminor = baseminor;   //cd->baseminor = 0;
			cd->minorct = minorct;           //cd->minorct  = 3;
			strlcpy(cd->name, name, sizeof(cd->name));
			i = major_to_index(major);   //i = 250;
/*for (cp = &chrdevs[250]; *cp; cp = &(*cp)->next)*/
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 (*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;
 }
附录1-1:

#define CHRDEV_MAJOR_HASH_SIZE	255
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];	

附录1-2

 

前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2018 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号
返回

讲师博文

星创客 - 华清远见旗下高端IT培训品牌

当前位置: 星创客 > 学习资源 > 讲师博文 >

设备号注册过程分析
来源: 星创客 作者: 星创客 时间:2017-11-08

今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数: l register_chrdev_r...

今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数:

 

l  register_chrdev_region();

 

l  alloc_chrdev_region();

 

设备号的数据类型是dev_t类型,是一个无符号长整型,在32位操作系统中,它的大小是4个字节,32位,高12位用来存放主设备号,低20位用来存放次设备号。

 

通过主设备号和次设备号合成设备号的宏如下:

 

MKDEV(major,minor);

 

从设备号中提取主设备号和次设备号的宏如下:

 

MAJOR(dev);

 

MINOR(dev);

 

接下来我们就来分析设备号注册过程:

 

一、自动分配设备号:
 

1.  调用内核提供的自动分配设备号函数,分配设备号 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)  
2. alloc_chrdev_region函数中继续调用如下函数,进行进一步申请 
struct char_device_struct *cd =  __register_chrdev_region(0, baseminor, count, name);		
/*在该函数中进行判断,如果major==0,执行if内的语句*/			
if (major == 0) {                     //自动分配时major == 0
/*
	*chrdevs是一个结构体指针数组,见附录1-1
*作用是从struct char_device_struct类型的结构体指针数组中找到一个空的结构体指针
*/
	for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {  // for(i=254;i>0;i--)
		if (chrdevs[i] == NULL)
		break;
	}
if (i == 0) {      //i == 0 ,说明,存放设备号的结构体指针数组已经用完
		ret = -EBUSY;
		goto out;
	}
major = i;         //否则,主设备号 = i
	ret = major;
cd->major = major;                       // 将主设备号赋值给cd->major
	cd->baseminor = baseminor;       // 将起始次设备号赋值cd->baseminor
	cd->minorct = minorct;    // 将传入的第三个参数赋值给程cd->minorct,
                                                     // 表示申请设备号的个数
	strlcpy(cd->name, name, sizeof(cd->name));  //设备名拷贝
/*
*struct char_device_struct *cd, **cp;
*cp存放的是结构体指针的地址,基于当前的条件,*cp == NULL ,所以for循环条件不成立
*/
--------------------------------------不执行-------------------------------------
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 == NULL ,所以条件不成立*/
---------------------------------------不执行-----------------------------------------
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;                  //cd ->next = NULL
*cp = cd; 			//*cp[chrdevs[i]] , cd 结构体在上面赋值的
mutex_unlock(&chrdevs_lock);
return cd;
回到alloc_chrdev_region函数的*dev = MKDEV(cd->major, cd->baseminor);
实际上就申请了一个主设备号,次设备号
二、用户指定设备号注册(eg:major = 250;count=3)
1. int register_chrdev_region(dev_t from, unsigned count, const char *name);
2. 在该函数中进行如下操作:
dev_t to = from + count;      // dev_t to =  MKDEV(dev_major,dev_minor) +3;
dev_t n, next;
/*如果申请的设备编号范围跨越了主设备号,
*它会把分配范围内的编号按主设备号分割
 *成较小的子范围,并在每个子范围上调用
 *__register_chrdev_region() 。如果其中
*有一次分配失败的话,那会把之前成功分配的都全部退回*/
// for(n = MKDEV(dev_major,dev_minor); n<to;n=next)循环一次(next = to)
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);         // next = MKDEV(250+1,0);
	if (next > to)                       // if(MKDEV(251,0)>MKDEV(250,0)+3) [成立]
	next = to;                          // next = MKDEV(250,0)+3
cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name);
				//__register_chrdev_region(250, 0,3, "xxx_demo");
	       {
 cd->major = major;           //cd->major = 250;
			cd->baseminor = baseminor;   //cd->baseminor = 0;
			cd->minorct = minorct;           //cd->minorct  = 3;
			strlcpy(cd->name, name, sizeof(cd->name));
			i = major_to_index(major);   //i = 250;
/*for (cp = &chrdevs[250]; *cp; cp = &(*cp)->next)*/
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 (*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;
 }
附录1-1:

#define CHRDEV_MAJOR_HASH_SIZE	255
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];	

附录1-2

 

相关推荐

全国咨询热线:400-611-6270

?2004-2018华清远见教育科技集团 版权所有 京ICP备16055225号 京公海网安备11010802025203号