kobject, kobj_type, kset简介
kobject是linux设备模型中最基本的结构。要说明的是,linux内核虽然由C语言编写,但是内核在实现时,到处都体现出开发者面向对象的思想。所以,kobject也可以理解为所有驱动对象的基类。后面用到的驱动对象,几乎都是kobject的派生类。作为基类的kobject并不关心自己是如何实现的,所以,在内核中,没有用kobject直接定义的变量,kobject只是作为一个抽象的基类而存在。一般都是将kobject嵌入到另一个结构,这个结构就可以看做是kobject的一个子类。而kobject的子类会比较关心kobject的属性和方法。
kobject包含一个名字和一个引用计数器,这个引用计数器会记录由kobject派生的内核对象被引用的次数。所以,一个结构最多只能包含一个kobject对象,否则,该结构的引用计数会乱套的。
上面提到,关心kobject实现的并不是kobject自身而是包含kobject的结构,所以当不同的结构包含kobject后,kobject的属性会不同,kobject销毁时所做的操作会不同,kobject所表现出的类型也会不同。所以,kobject中包含了一个叫作kobj_type的结构。kobj_type的目标就是为不同类型的kobject提供不同的属性以及销毁方法。
有时候,某个设备的可能具有多个kobject的子类对象,或者某些设备具有相同的特性,为了便于管理,应该把这些对象统一放入一个容器中。这里要用到的容器就是kset。kset只是kobject的一个集合。对应到linux文件系统中,一个kset就是/sys下的一个文件夹。
kobject详细介绍
先看看kobject的结构:
struct kobject {
const char *name; //可以给kobject起一个名字
struct list_head entry;
struct kobject *parent; //kobject的父指针
struct kset *kset; //kobject所属的kset
struct kobj_type *ktype;
struct sysfs_dirent *sd; //kobject在sysfs中的层次结构,关于sysfs这里不做讨论
struct kref kref; //引用计数
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的操作:
extern int kobject_set_name(struct kobject *kobj, const char *name, ...)
__attribute__((format(printf, 2, 3)));
extern int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs);
上两个函数为设定kobject的名字。虽然从上面的结构定义中可以看到kobject的名字就是成员name所指向的字符串,但为kobject设定名字时,还是函数kobject_set_name,这样可以提高代码的可移植性。
static inline const char *kobject_name(const struct kobject *kobj)
{
return kobj->name;
}
extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
此函数初始化kobject中的entry,kref,将state_in_sysfs设为0,state_add_uevent_sent设为0,state_remove_uevent_sent设为0,state_initialized设为1,并根据传入的ktype设定kobj->ktype,而且ktype不能为空。
extern int __must_check kobject_add(struct kobject *kobj,
struct kobject *parent,
const char *fmt, ...);
此函数将一个已初始化好的kobject加入系统,第一个参数是需要加入的kobject,第二个参数是kobject的父结点,第三个参数是kobject的名字。若parent为空,且kobj->kset不为空,则将kobj->prent设为&kobj->kset->kobj。此函数的流程图如下:
extern int __must_check kobject_init_and_add(struct kobject *kobj,
struct kobj_type *ktype,
struct kobject *parent,
const char *fmt, ...);
从名字就可以看出来,这个函数是kobject_init和kobject_add的组合
extern void kobject_del(struct kobject *kobj);
此函数只是将kobject从sysfs和kset中去掉,并不是删除kobject,删除kobject得靠kobject->ktype->release才行。kobject_del在把kobject从kset中去掉后,会把kset的引用值减1。
extern struct kobject * __must_check kobject_create(void);
动态分配一个kobject,将kobject的ktype设为&dynamic_kobj_ktype后,并初始化之。
extern struct kobject * __must_check kobject_create_and_add(const char *name,
struct kobject *parent);
从名字就可以看出来,这个函数是kobject_create和kobject_add的组合。
以上两个函数的流程图如下:
extern int __must_check kobject_rename(struct kobject *, const char *new_name);
此函数可以为一个已经添加到sysfs中的kobject重命名,同时也会改变kobject在sysfs中对应的文件名。此函数在重命名之前会先把kobject的引用值加1,在改完名后,会再把kobject的引用值减1。
extern int __must_check kobject_move(struct kobject *, struct kobject *);
此函数可以改变一个kobject的parent,并同时更新kobject在sysfs中的路径,更换完parent之后,函数会把kobject以前的parent的引用值减1。
extern struct kobject *kobject_get(struct kobject *kobj);
此函数为kobject的引用值加1。
extern void kobject_put(struct kobject *kobj);
此函数为kobject的引用值减1,若引用值减为0,则销毁此kobject。
上面这两个函数的流程图如下:
extern char *kobject_get_path(struct kobject *kobj, gfp_t flag);
获取kobject在sysfs中的路径。
ktype详细介绍
struct kobj_type {
void (*release)(struct kobject *kobj); //kobject销毁时所调用的函数,由kobject_put调用
const struct sysfs_ops *sysfs_ops; //对于attribute的操作
struct attribute **default_attrs;
};
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
kobj_type中的后两个成员表示了kobject在sysfs中的各个属性,及各个属性的操作方法。
需要说明的是,每一个kobject都必须有一个release函数,而已每个kobject不能在release函数被调用之前被销毁。release的作用是清除为kobject分配的内存空间,不过在release被调用时,kobject->name是有效的,但是,release函数不能改变name,因为name的释放是在函数kobject_cleanup中做的。
kset详细介绍
struct kset {
struct list_head list; //此链表中保存了kset中所有的kobject
spinlock_t list_lock; //当遍历list时,使用的锁
struct kobject kobj; //居然递归嵌套,我晕。。。。
const struct kset_uevent_ops *uevent_ops; //暂时不讨论uevent
};
kset关联的操作:
extern void kset_init(struct kset *kset);
初始化kset中各个成员。
extern int __must_check kset_register(struct kset *kset);
此函数首先将kset加入sysfs(kset加入sysfs是通过kset中的成员kobject实现的,其实加入sysfs的不是kset本身,而是kset的成员kobject,可以通过kobject和container_of找到kset)然后发出一个KOBJ_ADD的uevent。
extern struct kset * __must_check kset_create_and_add(const char *name,
const struct kset_uevent_ops *u,
struct kobject *parent_kobj);
此函数只是kset_create和kset_register的组合,其中kset_create会动态生成一个kset,并将其初始化。如果想简单地生成一个kset并将其加入系统,可以调用此函数。
以上三个函数的流程图如下:
extern void kset_unregister(struct kset *kset);
此函数将kset从sysfs中移出,也就是将kset的成员kobject从sysfs中移出。