linux字符设备完全分析(一)
来源:互联网 发布:编程算法有哪些 编辑:程序博客网 时间:2024/06/07 21:11
终于。。。
我开始写博客了。。。。。
因为懒。。。所以拖了很久。。。。。
以后要多坚持。。。。。。。。。
今天主要是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字符设备完全分析(二)
- linux 字符设备(一)
- Linux设备驱动--字符设备(一)
- 简单字符设备驱动程序分析(一)
- LDD3中scull字符设备源代码完全解析(一)
- Linux内核修炼之字符设备分析一
- Linux字符设备驱动入门(一)
- linux字符设备驱动框架(一)
- linux高级字符设备驱动(一 设备Ioctl控制)
- linux设备驱动(一)---字符设备之led驱动
- 《Linux设备驱动程序》读书笔记:字符设备驱动程序(一)
- Linux设备之字符驱动设备(一)
- Linux设备驱动之字符设备(一)
- linux 字符设备驱动(一)模块与设备
- Linux字符设备驱动分析
- Linux I2C驱动完全分析(一)
- Linux I2C驱动完全分析(一)
- 入学考试 -- 蓝桥杯
- 二叉树中的最大路径和
- NoteHighLight--OneNote代码高亮插件
- Struts 2中的constant配置详解
- 错误:编码GBK的不可映射字符
- linux字符设备完全分析(一)
- 51nod 1338 找格子 费用流
- Linux virtualbox 开机进不去图形化界面,停留在文本界面解决方法
- 2017-12-21php学习基础巩固第三天
- 数组去重
- 自定义View的宽高获取
- 数据结构实验之排序七:选课名单
- poj3734 Blocks(指数生成函数)
- 表达式期望结果的数目 动态规划