1.Kobject结构
每个在内核中注册的kobject都对应于sysfs文件系统中的一个目录。
struct kobject {
const char *name; //kobject的名称
struct list_head entry; //kobject 之间的双向链表,与所属的kset形成环形链表
struct kobject *parent; //父kobject结构体
struct kset *kset; //指向所属的kset
struct kobj_type *ktype; //kobject的类型描述符,负责对该kobject类型进行跟踪的struct kobj_type的指针
struct sysfs_dirent *sd; //sysfs文件目录,表示该内核对象在sys中对应的目录项的实例
struct kref kref; //kobject引用计数
unsigned int state_initialized:1; //kobject是否初始化
unsigned int state_in_sysfs:1;//该内核对象在sysfs中已建立入口点
unsigned int state_add_uevent_sent:1;当该对象发生变化时,不通过其所属的kset向用户空间发送uevent信号
unsigned int state_remove_uevent_sent:1;
struct dentry * dentry;/*sysfs文件系统中与该对象对应的文件节点路径指针*/
wait_queue_head_t poll;/*等待队列头*/
};
假如有一个设备A。音频设备则应该有音量属性,则音量属性将在A设备目录下有个音量属性文件。在使用设备时,如果要改变音量大小,则可以写属性文件入音量指。得到音量大小时,可以读取属性文件中的音量值。
2.Kobject初始化
初始化一个kobject结构体变量,kobject_init函数(lib/kobject.c),调用此函数前应先将kobject变量成员全部置0
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
kobject_init_internal(kobj); //进一步初始化kobj内部成员
kobj->ktype = ktype; //将参数中传来的ktype变量赋值给kobj的ktype变量。
return;
error:
printk(KERN_ERR "kobject (%p):%s\n", kobj, err_str);
dump_stack();//内核查看函数调用流程
}
分析kobject_init_internal函数(lib/kobject.c),此函数主要设置一些kobj中的一些变量
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj) //kobj是否为空
return;
kref_init(&kobj->kref); //增加kobject的引用计数,kref_set(kref, 1);
INIT_LIST_HEAD(&kobj->entry); //初始化kobj的链表
kobj->state_in_sysfs = 0; //kobject还没有注册到sysfs中
kobj->state_add_uevent_sent = 0; //
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
3.kobj_type
1、 它是被嵌入kobject对象的类型,每个kobject对象应有相对应的ktype类型。
2、它的作用:kobject创建和销毁时,应该做哪些操作。
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函数指针。
sysfs_ops
指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两 个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属 性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。
default_attrs
指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认 属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应 的作为文件而导出。其定义如下:
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件 名就是name。文件读写的方法对应于kobj_type中的sysfs_ops。
release 函数和 kobject 类型
引用计数不由创建 kobject 的代码直接控制,当 kobject 的最后引用计数消失时,必须异步通知,而后kobject中ktype所指向的kobj_type结构体包含的release函数会被调用。通常原型如下:
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
每个 kobject 必须有一个release函数, 并且这个 kobject 必须在release函数被调用前保持不变( 稳定状态 ) 。这样,每一个 kobject 需要有一个关联的 kobj_type 结构,指向这个结构的指针能在 2 个不同的地方找到:
(1)kobject 结构自身包含一个成员(ktype)指向kobj_type ;
(2)如果这个 kobject 是一个 kset 的成员, kset 会提供kobj_type 指针。
4.将kobject注册进sysfs系统
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
retval = kobject_add(kobj, parent, "%s", name);
return kobj;
}
看看kobject_add()实现流程:
kobject_add()->kobject_add_varg()->kobject_set_name_vargs()->kobject_add_internal()->create_dir()->sysfs_create_dir() ; 从这个流程可以看到注册一个kobject实际上就是在sysfs文件系统中创建一个目录.前面几个函数都是较简单的,这里主要看看kobject_add_internal()函数:
158 static int kobject_add_internal(struct kobject *kobj)
159 {
160 int error = 0;
161 struct kobject *parent;
172
173 parent = kobject_get(kobj->parent);
174
176 if (kobj->kset) {
177 if (!parent)
178 parent = kobject_get(&kobj->kset->kobj);
179 kobj_kset_join(kobj);
180 kobj->parent = parent;
181 }
182 error = create_dir(kobj);
205 kobj->state_in_sysfs = 1;
206
207 return error;
208 }
判断kobj的名字,我们的目录是要有名字的. ,增加kobj父亲的引用计数
如果kobj设置了kset, 在kobj未指定父对象的情况下,把ket->kobj设为这个kobj的父亲,并将kobj对象添加到kset的kobj list中.
create_dir(),就是在sysfs下创建一个目录文件撒.
我们在看一下如何将kobject注册进sysfs系统中。使用函数kobject_init_and_add()函数将一个kobject注册进sysfs系统,在/sys中表现为生成一个相应的目录。
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;
kobject_init(kobj, ktype); //调用初始化函数先初始化kobject变量
va_start(args, fmt); //解析可变参数列表
retval = kobject_add_varg(kobj, parent, fmt, args); //给kobject添加参数,并且将其添加到sysfs系统。
va_end(args); //结束解析参数列表
return retval;
}
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs); //设置kobject的名称
kobj->parent = parent; //设置kobject的父kobject
return kobject_add_internal(kobj); //添加kobject
}
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
parent = kobject_get(kobj->parent); //获得父kobject,并增加父kobject的引用计数
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) { //是否有kset集合
if (!parent) //如果没有父kobject则用kset中的kobject对象
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj); //将kobject添加进它关联的kset的list链表中。
kobj->parent = parent; //设置父koject
}
error = create_dir(kobj); //创建kobject的相应目录
kobj->state_in_sysfs = 1; //标记为已经注册进sysfs
return error;
}
sysfs创建目录函数create_dir,在lib/kobject.c
static int create_dir(struct kobject *kobj)
{
int error = 0;
if (kobject_name(kobj)) {
error = sysfs_create_dir(kobj); //在sysfs中创建目录,将来有时间了可以分析下sysfs子系统。
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
}
}
return error;
}
还有一个函数kobject_add,也可以添加一个kobject,它只是没有kobject_init这一步。
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}