概述
终于。。。
我开始写博客了。。。。。
因为懒。。。所以拖了很久。。。。。
以后要多坚持。。。。。。。。。
今天主要是linux 字符设备分析,既然是分析,那就要往深了分析才有意思。。
字符设备的表示当然是指这个struct cdev结构。let us see。。。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
kobj用来表示sys下的目录,owner模块使用者指针,一般赋值为THIS_MODULE,第三个参数很关键file_operations,是用户层调用open、release、write、read函数时。传到内核层处理,第四个结构list表示的是一个链表节点。第五个是设备号,没什么讲的。第六个是count
怎么分配结构体cdev呢,有两种方式,一种是静态定义一个 struct cdev my_cdev,然后在使用cdev_init函数去初始化该结构,第二种方式是利用cdev_alloc函数去动态申请。
有的人会先使用cdev_alloc函数去动态分配,在使用函数cdev_init去初始化,其实这种用法是不太正确的,为什么呢?且看下面分析。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
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;
}
看出相同之处和不同之处了吗,yes kobject_init这个函数是关键,关键在于ktype类型不同、
static struct kobj_type ktype_cdev_default = {
.release
= cdev_default_release,
};
static struct kobj_type ktype_cdev_dynamic = {
.release
= cdev_dynamic_release,
};
很明显,再看
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
cdev_purge(p);
}
static void cdev_dynamic_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
cdev_purge(p);
kfree(p);
}
对,多了一个kfree,也就是说alloc申请出来的cdev占用的内存空间可以在设备被卸载时自动释放。
下面来看linux 内核怎么管理字符设备号。
内核提供了两种方式,一种是alloc_chrdev_region另一个是register_chrdev_region,两个的底层实现如出一辙。下面来分析alloc_chrdev_region。
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(0, baseminor, count, name);
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) {//动态申请
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);
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;
}
}
cd->next = *cp;//挂接到哈希表里面
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
分析到这里,就该看一个有意思的数据结构了,也就是所谓的哈希表。
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];
这就是哈希表结构,chrdevs是一个指针数组,每一个元素都存储着一个字符设备,一看是static类型的,就知道这个chrdevs的每一项初始化后都为NULL,当一个新的设备来了之后通过主设备号来挂接到相应的chrdevs里面,主设备号就是哈希表的表头,而冲突域就用next指针来挂接。比如我注册了一个major = 254 baseminor= 3 的一个设备和major = 254 baseminor= 6的一个设备,这时候chrdevs[254]就应该指向major= 254 baseminor= 3的char_device_struct结构然后该结构的next指针又指向 major= 254 baseminor= 6的char_device_struct结构。
最后
以上就是有魅力日记本为你收集整理的linux字符设备完全分析(一)的全部内容,希望文章能够帮你解决linux字符设备完全分析(一)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复