难以理解的一部分——kobject的驱动模型
抽象建立在它之上——没有明显的开始的地方
本文代码查阅是Linux 3.4.0
Kobjects结构体
include/linux/kobject.h
struct kobject {
const char *name;
struct list_head entry; /*kobject 之间的双向链表,与所属的kset形成环形链表*/
struct kobject *parent; /*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
struct kset *kset; /*指向所属的kset*/
struct kobj_type *ktype; /*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
struct sysfs_dirent *sd; /*sysfs文件系统中与该对象对应的文件节点路径指针*/
struct kref kref; /*kobject 的引用计数*/
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
}
kobject是struct kobject类型的一个对象。Kobjects有个名字和一个引用计数。一个父指针(允许kobject对象被安排成层次结构)一个特定的类型,通常,表示在sysfs虚拟文件系统。一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
Kobject初始化:
/**
* kobject_init - initialize a kobject structure
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
*
* This function will properly initialize a kobject such that it can then
* be passed to the kobject_add() call.
*
* After this function is called, the kobject MUST be cleaned up by a call
* to kobject_put(), not by a call to kfree directly to ensure that all of
* the memory is cleaned up properly.
*/
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
kobj_type:
struct kobj_type {
void (*release)(struct kobject *kobj); //释放函数(驱动编写时提供),此函数会被kobject_put函数调用
struct sysfs_ops *sysfs_ops; //属性文件的操作函数(只有读和写操作)
struct attribute **default_attrs; //属性数组
};
讨论kobj_type和kobject的关系,就要先说说kobject的引用。引用一个kobject使用函数kobject_get()这个函数会增加kobject的引用并返回kobject的指针。增加其引用是通过其kobject中断哦kref变量完成的。对kobject的引用管理主要是为了知道被引用的情况,如引用不为0就不能销毁kobject对象,引用为0时则调用相应的释放函数等。
struct kobject *kobject_get(struct kobject *kobj)
{
if (kobj)
kref_get(&kobj->kref);
return kobj;
}
void kref_get(struct kref *kref)
{
WARN_ON(!atomic_read(&kref->refcount));
atomic_inc(&kref->refcount); //将kref中的这个原子变量加1
smp_mb__after_atomic_inc();
}
减少一个kobject对象的引用使用函数kobject_put()。当一个kobject对象的引用被减少到0时,程序就应该释放这个kobject相关的资源。所以在减少引用的函数中就应该有调用释放资源的相关代码,在下面内核代码中我们可以看到。
void kobject_put(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized) //若kobj没有初始化就不能减少其引用
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_put() is being "
"called.\n", kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release); //减少kref计数
}
}
int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
WARN_ON(release == NULL); //为空警告
WARN_ON(release == (void (*)(struct kref *))kfree); //如果release函数就是kfree,则警告(即release函数不能是简单的kfree)
if (atomic_dec_and_test(&kref->refcount)) { //递减原子变量并检查其值
release(kref); //回调release函数
return 1;
}
return 0;
}
那这个release函数在哪里保存呢,这就和kobj_type结构有关系了。上面我们可以看到kobj_type中有一个release函数指针,就是保存在这里。每一个kobject的ktype都指向一个kobj_type,它保存了这个kobject类型的release函数指针。
Kset集合:
1.Kset是具有相同类型的kobject集合。一个Kset集合可以表示在/sys/drivers/目录下,表示一类驱动程序。kobject则表示一个具体的驱动目录。这样kset则可以将kobject组织成层次化结构。
struct kset {
struct list_head list; //这个链表存放这个kset关联的所有kobject
spinlock_t list_lock; //维护此链表的锁
struct kobject kobj; //内嵌的kobject。这样kset本身也是一个kobject也被表现为一个目录
struct kset_uevent_ops *uevent_ops; //支持热插拔事件的函数集
};
kset中的kobject对象,所有属于这个kset集合的kobject对象的parent指针,均指向这个内嵌的kobject,也即表示在一个kset集合中的kobject是相同类型的他们有相同的parent对象。kset的引用计数也就是内嵌的kobject的引用计数。
所以kobject和kset的关系简单来讲,就是
1.kset是kobject的一个顶层容器,它包含了相同类型的kobject,kset中有链表成员保存所有的kobject指向。
2.kobject中的kset指针指向了一个kset
3.kset中有kobject对象,表明了kset也可以有kobject相关的操作。
4.kset链表中的kobject对象的parent指针一般都指向kset内嵌的kobject对象。见图kset和kobject关系
kset_uevent_ops热插拔事件
热插拔事件是用内核空间发送到用户空间的通知。表明内核中的某些配置已经发生变化。用户空间则会根据这些信息做相应的处理。例如,U盘插入USB系统时,会产生一个热插拔事件,内核会捕捉到这个热插拔事件,然后调用/sbin/hotplug程序,该程序通知加载驱动程序来相应U盘的插入动作。
热插拔函数集的定义在include/linux/koject.h中
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj); //事件过滤函数
const char *(*name)(struct kset *kset, struct kobject *kobj); //事件名称函数
int (*uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);
//uevent函数可在热插拔程序执行前,向环境变量写值
};