1.cdev_init()。初始化cdev对象的一些成员
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;
}
首先把传进来的cdev对象所占的内存清零
然后初始化cdev->list_head链表
接着把cdev->kobj也初始化,调用的kobject_init注释如下:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype) //@kobj :要被初始化的kobject
{ //@ktype:该kobj要初始化的ktype成员
char *err_str;
if (!kobj) { //如果kobj为空
err_str = "invalid kobject pointer!"; //打印"invalid kobject pointer!"
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";//如果ktype为空
goto error; //打印"must have a ktype to be initialized properly!\n"
}
if (kobj->state_initialized) { //如果kobj已经被初始化过了
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized " //打印错误信息:尝试初始化一个已经初始化过的kobject
"object, something is seriously wrong.\n", kobj);
dump_stack(); //在控制台打印Oops信息
}
kobject_init_internal(kobj); //kobj初始化
kobj->ktype = ktype; //把该kobj的ktype成员初始化为ktype
return; //返回
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); //打印错误信息
dump_stack(); //在控制台打印Oops信息
}
EXPORT_SYMBOL(kobject_init);
其中的ktype_cdev_default就是kobject_init函数的第二个参数,ktype_cdev_default的定义如下:
static struct kobj_type ktype_cdev_default = {
.release = cdev_default_release,
};
kobj_type类型中的release()是在kobject释放时调用的,但是看了cdev_default_release的实现,发现并没有释放这个kobject,因为cdev不是动态创建的,注意看cdev_alloc,它在调用kobject_init的时候,kobj_type成员就变成了ktype_cdev_dynamic,它里面的release函数就释放了这个cdev对象。
接下来看cdev_default_release做了什么:
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
struct kobject *parent = kobj->parent;
cdev_purge(p);
kobject_put(parent);
}
首先获得了该kobj所在的cdev对象,然后获得了这个kobj的父对象。接着调用cdev_purge(p),这个cdev_purge()做了什么呢?
static void cdev_purge(struct cdev *cdev)
{
spin_lock(&cdev_lock);
while (!list_empty(&cdev->list)) {
struct inode *inode;
inode = container_of(cdev->list.next, struct inode, i_devices);
list_del_init(&inode->i_devices);
inode->i_cdev = NULL;
}
spin_unlock(&cdev_lock);
}
首先是上锁,然后一个while循环,循环的条件是cdev里面的list_head链表非空,循环体内,首先获得链表的下一个成员所在的inode对象,然后利用inode->i_devices(struct list_head类型),将该节点从所在的链表删除,最后把该inode的i_cdev赋空。这个while循环是将挂载在cdev的list_head链表的所有成员删除。
回到cdev_default_release,接下来调用了kobject_put(parent),这一操作是将该kobj的父对象的kref减一,如果kref==0,调用parent的kobj_type成员中的release函数将parent删除。
2.cdev_add()
cdev_add函数主要是将cdev加入到cdev_map中,然后将cdev的kobject成员的parent对象的kref成员加1(有点绕),具体函数实现如下:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
该函数首先用设备号dev和count给p的成员赋值,接着调用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, 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;
p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
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;
}
首先是kobj_map成员,内核定义了一个kobj_map类型的对象cdev_map。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, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
probes也是一个哈希表,实现原理和设备号的哈希表chrdevs几乎完全一样。下面看具体实现。
首先计算主设备号个数,和register_chrdev_region中一样,防止range过大,分配的主设备号不止一个。然后定义一个index等于主设备号,申明一个struct probe类型的指针,调用kmalloc_array为p分配内存,如果p还为空,返回错误代码,接下来用传入的参数为p的成员初始化。接着调用cdev_map中的互斥锁上锁,接下来把p插入哈希表probes中,插入规则还是range小的在前。