先看kobj_map相关的代码
涉及到的文件
<linux/kobj_map.h>
<drivers/base/map.c>
- typedef struct kobject *kobj_probe_t(dev_t, intint *, voidvoid *);
- struct kobj_map;
- int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *, kobj_probe_t *, int (*)(dev_t, voidvoid *), voidvoid *);
- void kobj_unmap(struct kobj_map *, dev_t, unsigned long);
- struct kobject *kobj_lookup(struct kobj_map *, dev_t, intint *);
- struct kobj_map *kobj_map_init(kobj_probe_t *, struct mutex *);
typedef struct kobject *kobj_probe_t(dev_t, int *, void *); struct kobj_map; int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *, kobj_probe_t *, int (*)(dev_t, void *), void *); void kobj_unmap(struct kobj_map *, dev_t, unsigned long); struct kobject *kobj_lookup(struct kobj_map *, dev_t, int *); struct kobj_map *kobj_map_init(kobj_probe_t *, struct mutex *);
先看kobj_map结构体
- struct kobj_map {
- struct probe {
- struct probe *next; /* 这样形成了链表结构 */
- dev_t dev; /* 设备号 */
- unsigned long range; /* 设备号的范围 */
- struct module *owner;
- kobj_probe_t *get;
- int (*lock) (dev_t, voidvoid *);
- voidvoid *data; /* 指向struct cdev对象 */
- } *probes[255];
- struct mutex *lock;
- }
struct kobj_map { struct probe { struct probe *next; /* 这样形成了链表结构 */ dev_t dev; /* 设备号 */ unsigned long range; /* 设备号的范围 */ struct module *owner; kobj_probe_t *get; int (*lock) (dev_t, void *); void *data; /* 指向struct cdev对象 */ } *probes[255]; struct mutex *lock; }
结构体中有一个互斥锁lock,一个probes[255]数组,数组元素为struct probe的指针。
根据下面的函数作用来看,kobj_map结构体是用来管理设备号及其对应的设备的。
kobj_map函数就是将指定的设备号加入到该数组,kobj_lookup则查找该结构体,然后返回对应设备号的kobject对象,利用
利用该kobject对象,我们可以得到包含它的对象如cdev。
struct probe结构体中的get函数指针就是用来获得kobject对象的,可能不同类型的设备获取的方式不同,我现在就看过cdev的exact_match函数。
kobj_map函数
- int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, struct module *module, kobj_probe_t *probe, int (*lock)(dev_t, voidvoid *), voidvoid *data)
- {
- unsigned n = MAJOR(dev+range-1) - MAJOR(dev) + 1;
- unsigned index = MAJOR(dev);
- unsigned i;
- struct probe *p;
- if (n > 255) /* 若n > 255,则超出了kobj_map中probes数组的大小 */
- n = 255;
- p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); /* 分配n个struct probe */
- if(p == NULL)
- return -ENOMEM;
- for(i = 0; i < n; i++, p++) { /* 用函数的参数初始化probe */
- p->owner = module;
- p->get = probe;
- p->lock = lock;
- p->dev = dev;
- p->range = range;
- p->data = data;
- }
- mutex_lock(domain->lock);
- for(i = 0, p-=n; i < n; i++, p++, index++) {
- struct probe **s = &domain->probes[index % 255];
- while(*s && (*s)->range < range)
- s = &(*s)->next;
- p->next = *s;
- *s = p;
- }
- mutex_unlock(domain->lock);
- return 0;
- }
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, struct module *module, kobj_probe_t *probe, int (*lock)(dev_t, void *), void *data) { unsigned n = MAJOR(dev+range-1) - MAJOR(dev) + 1; unsigned index = MAJOR(dev); unsigned i; struct probe *p; if (n > 255) /* 若n > 255,则超出了kobj_map中probes数组的大小 */ n = 255; p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); /* 分配n个struct probe */ if(p == NULL) return -ENOMEM; for(i = 0; i < n; i++, p++) { /* 用函数的参数初始化probe */ p->owner = module; p->get = probe; p->lock = lock; p->dev = dev; p->range = range; p->data = data; } mutex_lock(domain->lock); for(i = 0, p-=n; i < n; i++, p++, index++) { struct probe **s = &domain->probes[index % 255]; while(*s && (*s)->range < range) s = &(*s)->next; p->next = *s; *s = p; } mutex_unlock(domain->lock); return 0; }
dev_t的前12位为主设备号,后20位为次设备号。
n = MAJOR(dev + range - 1) - MAJOR(dev) + 1 表示设备号范围(dev, dev+range)中不同的主设备号的个数。
通常n的值为1。
从代码中的第二个for循环可以看出kobj_map中的probes数组中每个元素为一个struct probe链表的头指针。
每个链表中的probe对象有(MAJOR(probe.dev) % 255)值相同的关系。若主设备号小于255, 则每个链表中的probe都有相同的主设备号。
链表中的元素是按照range值从小到大排列的。
while循环即是找出该将p插入的位置。
kobj_unmap函数
- void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
- {
- unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
- unsigned index = MAJOR(dev);
- unsigned i;
- struct probe *found = NULL;
- if (n > 255)
- n = 255;
- mutex_lock(domain->lock);
- for (i = 0; i < n; i++, index++) {
- struct probe **s;
- for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
- struct probe *p = *s;
- if (p->dev == dev && p->range == range) {
- *s = p->next;
- if (!found)
- found = p;
- break;
- }
- }
- }
- mutex_unlock(domain->lock);
- kfree(found);
- }
void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range) { unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; unsigned index = MAJOR(dev); unsigned i; struct probe *found = NULL; if (n > 255) n = 255; mutex_lock(domain->lock); for (i = 0; i < n; i++, index++) { struct probe **s; for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) { struct probe *p = *s; if (p->dev == dev && p->range == range) { *s = p->next; if (!found) found = p; break; } } } mutex_unlock(domain->lock); kfree(found); }
在16行,找到对应设备号dev和range指定的probe对象后,退出,然后kfree释放空间。
kobj_lookup函数
- struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, intint *index)
- {
- struct kobject *kobj;
- struct probe *p;
- unsigned long best = ~0UL;
- retry:
- mutex_lock(domain->lock);
- for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
- struct kobject *(*probe)(dev_t, intint *, voidvoid *);
- struct module *owner;
- voidvoid *data;
- if (p->dev > dev || p->dev + p->range - 1 < dev)
- continue;
- if (p->range - 1 >= best)
- break;
- if (!try_module_get(p->owner))
- continue;
- owner = p->owner;
- data = p->data;
- probe = p->get;
- best = p->range - 1;
- *index = dev - p->dev; /* 这个是用来干嘛的? */
- if (p->lock && p->lock(dev, data) < 0) {
- module_put(owner);
- continue;
- }
- mutex_unlock(domain->lock);
- kobj = probe(dev, index, data);
- /* Currently ->owner protects _only_ ->probe() itself. */
- module_put(owner);
- if (kobj)
- return kobj;
- goto retry;
- }
- mutex_unlock(domain->lock);
- return NULL;
- }
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) { struct kobject *kobj; struct probe *p; unsigned long best = ~0UL; retry: mutex_lock(domain->lock); for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) { struct kobject *(*probe)(dev_t, int *, void *); struct module *owner; void *data; if (p->dev > dev || p->dev + p->range - 1 < dev) continue; if (p->range - 1 >= best) break; if (!try_module_get(p->owner)) continue; owner = p->owner; data = p->data; probe = p->get; best = p->range - 1; *index = dev - p->dev; /* 这个是用来干嘛的? */ if (p->lock && p->lock(dev, data) < 0) { module_put(owner); continue; } mutex_unlock(domain->lock); kobj = probe(dev, index, data); /* Currently ->owner protects _only_ ->probe() itself. */ module_put(owner); if (kobj) return kobj; goto retry; } mutex_unlock(domain->lock); return NULL; }
对cdev_add函数,这里的p->probe函数即是exact_match, p->lock为exact_lock函数。
kobj_map_init函数
- struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
- {
- struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
- struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
- int i;
- if ((p == NULL) || (base == NULL)) {
- kfree(p);
- kfree(base);
- return NULL;
- }
- base->dev = 1;
- base->range = ~0;
- base->get = base_probe;
- for (i = 0; i < 255; i++)
- p->probes[i] = base;
- p->lock = lock;
- return p;
- }
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock) { struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); int i; if ((p == NULL) || (base == NULL)) { kfree(p); kfree(base); return NULL; } base->dev = 1; base->range = ~0; base->get = base_probe; for (i = 0; i < 255; i++) p->probes[i] = base; p->lock = lock; return p; }
在初始化一个kobj_map对象时,将probes指针全部指向同一个base。
下面是cdev部分。
文件:
<linux/cdev.h>
<fs/char_dev.c>
cdev.h
- struct cdev {
- struct kobject kobj;
- struct module *owner;
- const struct file_operations *ops;
- struct list_head list;
- dev_t dev;
- unsigned int count;
- }
- void cdev_init(struct cdev *, const struct file_operations *);
- struct cdev *cdev_alloc(void);
- void cdev_put(struct cdev *p);
- int cdev_add(struct cdev *, dev_t, unsigned);
- void cdev_del(struct cdev *);
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; } void cdev_init(struct cdev *, const struct file_operations *); struct cdev *cdev_alloc(void); void cdev_put(struct cdev *p); int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *);
cdev_init函数
此函数首先调用kobject_init初始化cdev中的kobj,然后将cdev中的ops赋值。
cdev_alloc函数
先kzalloc分配一个cdev,然后用kobject_init初始化kobj
cdev_put函数
- void cdev_put(struct cdev *p)
- {
- if (p) {
- struct module *owner = p->owner;
- kobject_put(&p->kobj);
- module_put(owner);
- }
- }
void cdev_put(struct cdev *p) { if (p) { struct module *owner = p->owner; kobject_put(&p->kobj); module_put(owner); } }
此函数调用kobject_put和module_put,好像它们的作用就是减少引用计数
cdev_add函数
- int cdev_add(struct cdev *p, dev_t dev, unsigned count)
- {
- p->dev = dev;
- p->count = count;
- return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
- }
int cdev_add(struct cdev *p, dev_t dev, unsigned count) { p->dev = dev; p->count = count; return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); }
主要是调用kobj_map将cdev放入cdev_map中。
cdev_del函数
- static void cdev_unmap(dev_t dev, unsigned count)
- {
- kobj_unmap(cdev_map, dev, count);
- }
- void cdev_del(struct cdev *p)
- {
- cdev_unmap(p->dev, p->count);
- kobject_put(&p->kobj);
- }
static void cdev_unmap(dev_t dev, unsigned count) { kobj_unmap(cdev_map, dev, count); } void cdev_del(struct cdev *p) { cdev_unmap(p->dev, p->count); kobject_put(&p->kobj); }
这就不用说啥了。
LDD3上说“只要cdev_add返回了,我们的设备就‘活’了,它的操作就会被内核调用",那么这句奇妙的话到底是个什么意思?
下面是我目前了解的情况
据说在open一个字符设备文件时,最终总会调用chrdev_open。
下面是该函数的源码
注意inode->i_rdev中保存了设备编号,inode->icdev指向了cdev结构。
- static int chrdev_open(struct inode *inode, struct file *filp)
- {
- struct cdev *p;
- struct cdev *new = NULL;
- int ret = 0;
- spin_lock(&cdev_lock);
- p = inode->i_cdev;
- if (!p) {
- struct kobject *kobj;
- int idx;
- spin_unlock(&cdev_lock);
- kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
- if (!kobj)
- return -ENXIO;
- new = container_of(kobj, struct cdev, kobj); /* 找到字符设备的cdev */
- spin_lock(&cdev_lock);
- /* Check i_cdev again in case somebody beat us to it while
- we dropped the lock. */
- p = inode->i_cdev;
- if (!p) {
- inode->i_cdev = p = new;
- list_add(&inode->i_devices, &p->list);/* ZXG: 这是啥? */
- new = NULL;
- } else if (!cdev_get(p))
- ret = -ENXIO;
- } else if (!cdev_get(p))
- ret = -ENXIO;
- spin_unlock(&cdev_lock);
- cdev_put(new);
- if (ret)
- return ret;
- ret = -ENXIO;
- filp->f_op = fops_get(p->ops);
- if (!filp->f_op)
- goto out_cdev_put;
- if (filp->f_op->open) {
- ret = filp->f_op->open(inode, filp); /* 调用cdev->ops中的open函数 */
- if (ret)
- goto out_cdev_put;
- }
- return 0;
- out_cdev_put:
- cdev_put(p);
- return ret;
- }
static int chrdev_open(struct inode *inode, struct file *filp) { struct cdev *p; struct cdev *new = NULL; int ret = 0; spin_lock(&cdev_lock); p = inode->i_cdev; if (!p) { struct kobject *kobj; int idx; spin_unlock(&cdev_lock); kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); if (!kobj) return -ENXIO; new = container_of(kobj, struct cdev, kobj); /* 找到字符设备的cdev */ spin_lock(&cdev_lock); /* Check i_cdev again in case somebody beat us to it while we dropped the lock. */ p = inode->i_cdev; if (!p) { inode->i_cdev = p = new; list_add(&inode->i_devices, &p->list);/* ZXG: 这是啥? */ new = NULL; } else if (!cdev_get(p)) ret = -ENXIO; } else if (!cdev_get(p)) ret = -ENXIO; spin_unlock(&cdev_lock); cdev_put(new); if (ret) return ret; ret = -ENXIO; filp->f_op = fops_get(p->ops); if (!filp->f_op) goto out_cdev_put; if (filp->f_op->open) { ret = filp->f_op->open(inode, filp); /* 调用cdev->ops中的open函数 */ if (ret) goto out_cdev_put; } return 0; out_cdev_put: cdev_put(p); return ret; }