1. kobject定义的头文件
#include
2. kobject所能处理的任务和它所支持的代码
a. 对象的引用记数。
b. sysfs表述
c. 数据结构关联
d. 热插拔事件处理
3. 如何通过一个kobject指针得到包函它的结构体指针
struct cdev
{
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
}
可以使用 container_of()宏定义将包含在cdev结构体中的、名为kp的kobject结构体指针经过转换得到
包含kp的结构体指针。如下:
struct cdev *device = container_of( kp, struct cdev, kobj );
4. kobject的初始化
a. 使用memset()将kobject清零
b. 调用kobject_init(),设置结构体内的一些成员
void kobject_init(struct kobject *kobj);
c. 设置kobject的名字
int kobject_set_name(struct kobject *kobj, const char *format, ...);
d. 其它成员:ktype, kset, parent.
5. 为包含kobject的结构体进行引用记数
kobject的一个重要函数是为包含它结构体设置引用记数。只要对象的引用记数存在,对象(以及支持它的代码)就必须继续存在。底层控制kobject的引用计数函数有:
struct kobject *kobject_get(struct kobject *kobj); // 增加引用计数
struct kobject *kobject_put(struct kobject *kobj); // 减少引用计数
6. kobject引用计数为零时的异步通知
当kobject的引用计数为零时,要只能异步地通知系统将其释放。通知是使用kobject的release方法实现的。
该方法通常的原型如下:
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj);
/******** 对该对象执行其它的清理工作,然后.... ********/
kfree(mine);
}
kobject的release()方法并未包含在kobject自身内,而在包含在与kobject相关联的一种称为ktype的kobj_type数据结构中。
如下:
struct kobj_type
{
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
}
每个kobject都需要一个与之对应的kobj_type结构。而这个结构体的指针可以通过如下宏得到。
struct kobj_type *get_ktype(struct kobject *kobj);
7. kobject的层次结构、kset和子系统
7.1 层次结构
通常,内核用kobjet将各个对象连接起来组成一个分层的结构体系,从而与模型化的子系统相匹配。
有两种独立的机制用于连接:parent指针和kset
在kobject结构的成员parent中,保存了另外一个kobject结构的指针,这个结构表示了分成层结构中的上一层节点。
比如一个kobject结构中表示了一个USB设备,它的parent指针可能指向了表示USB集线器的对象,而USB对象是
插在USB集线器上的。
对parent指针最重要的用途是在sysfs分层结构中定位对象。
7.2 kset
kset是相同类型结构的kobject的集合。因此kset的主要功能是包容。我们可以认为它是kobject的顶层容器类。在每个kset内
部包含了自己的kobject.
kset总是在sysfs中出现。一旦设置了kset并将它添加到系统中,将在sysfs中创建一个目录。而kobject不必在sysfs中表示,
但kset中的每个kobject成员都将在sysfs中得到表述。
kset还包含了一个子系统指针(称之为subsys).
a.将kobject添加到kset中
创建一个对象时,通常要把kobject添加到kset中去。这个过程分成两部。先把kobject的kset成员指向目的kset,然后将
kobject传递给下面的函数。
int kobject_add(struct kobject *kobj);
也可以使用如下函数,它是kobject_init()和kobjdect_add()的简单组合
extern int kobject_register(struct kobject *kobj);
b.从kset中删除kobject
在某些时候,可能不得不把kobject从kset中删除,以清除引用计数。可使用如下函数。
void kobject_del(struct kobject *kobj);
也可以使用kobject_unregister()函数,它是kobject_del()和kobjdect_put()的组合
c.kset上的操作
kset和kobject类似,它拥有初如化和设置接口。下面是这些函数
void kset_init(struct kset *kset);
int kset_add(struct kset *kset);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
d.kset 引用计数
struct kset *kset_get(struct kset *kset);
void kset_put(struct kset *kset);
e.设置kset 的名字
一个kset也拥有名字,它保存在内嵌的kobject中.因此,如果我们有一个名为my_kset的kset,可以使用下面的函数设置名字
kobject_set_name(&my_kset->kobj, "The name");
7.3 子系统
子系统通常显示在sysfs分层结构中的顶层.内核中的子系统包括block_subsys(对块设备来说是/sys/block)、devices_subsys(/sys/devices,设备分层结构的核心)以及内核所知晓的用于各种总线的子系统。下面的简单结构表示了一个子系统。
struct subsystem
{
struct kset kset;
struct rw_semaphore rwsem;
}
一个子系统其实是对kset和一个信号量的封装。每一个kset都必须属于一个子系统。子系统的成员帮助内核在分层结构中定位kset。在kset结构体中包含了subsys指针,因此通过kset结构,可以找到包含kset的每一个子系统。但我们无法直接从subsystem结构中找到子系统包含的多个kset。
通常使用下面的宏申明subsystem.该宏用name追加_subsys作为结构名而创建subsystem结构
decl_subsys(name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);
子系统拥有一个设置和销毁的函数列表:
void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys);
void subsys_put(struct subsystem *subsys);
8.低层sysfs操作
kobject是隐藏在sysfs虚拟文件系统后机制,对于sysfs中的每个目录,内核中都会存在一个对应的kobject.每个kobject都输出一个或者多个属性,它们在kobject的sysfs目录中表线为文件,其中的内容由内核生成.只要调用kobject_add()函数,就能在sysfs中显示kobject.
kobject在sysfs中入口始终是一个目录.分配给kboject的名字(使用kobjcet_set_name()函数)是sysfs中的目录名.这样,处于sysfs分层结构中相同部分的kobject必须有唯一的名字.sysfs入口在目录中 的位置对应kobject的parent指针.
8.1. kobject的默认属性
当创建kobject的时候,都会给kobject一系列默认属性.这些属性保存在kobj_type结构体中.下面是该结构体成员.
struct kobj_type
{
void (*release)(struct kobject *);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
}
default_attrs成员保存了属性列表,用于创建该类型的每一个kobject,sysfs_ops提供了实现这些属性的方法.default_attrs指向了一个包函attribute结构数组的指针:
struct attribute
{
char *name; // 属性的名字(在kobject的sysfs目录中显示)
struct module *owner; // 指向模块的指针
mode_t mode; // 属性保护位,通常为S_IRUGO
}
default_attrs数组说明了kobject都有些什么属性,但是没有告诉sysfs如果真正实现这些属性.这个任务交给了kobj_type->sysfs_ops成员.它所指向的结构体定义如下:
struct sysfs_ops
{
ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);
ssize_t (*store)(struct kobject *kobj, struct attribute *attr, char *buffer, size_t size);
}
当用户空间读取一个属性时,内核会使用指向kobject的指针和正确的属性结构来调用SHOW方法.该方法会将指定的值编码后放入缓冲区.
8.2. kobject非默认属性
我们可以根据需要对kobject内的属性进行添加和删除.往kobject中添加属性的函数:
int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
删除kobject中的属性的函数:
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
8.3. kobject的二进制属性
当在用户空间和设备之间传输不可改变的数据时,可能需要用到二进制属性.用于描述二进制属性的结构体如下:
struct bin_attribute
{
struct attribute attr;
size_t size;
ssize_t (*read)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
ssize_t (*store)(struct kobject *kobj, char *buffer, loff_t pos, size_t size);
}
创建二进制属性的函数:
int sysfs_create_bin_file(struct kobject *kobj, struct attribute *attr);
删除二进制属性的函数:
int sysfs_remove_bin_file(struct kobject *kobj, struct attribute *attr);
9.热插拔事件的产生
一个热插拔事件是从内核空间发送到用户空间的通知,它表明系统配置出现了变化.无论kobject是被创建还是被删除都会产生这种事件.热插拔事件会导致对/sbin/hotplug程序的调用.该程序通过加载驱动程序,创建设备节点,挂载分区,或者其它正确的动作来响应事件.当我们把kobject传递给kobject_add()或者kobject_del()时,才会真正产生这些事件.
对热插拔的实际控制,是由保存在kset_hotplug_ops结构中的函数完成的:
struct kset_hotplug_ops
{
int (*filter)(struct kset *kset, struct kobject *kobj);
char *(*name)(struct kset *kset, struct kobject *kobj);
int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size);
}
我们可以在kset结构的hotplug_ops成员中发现指向这个结构的结构体指针.
无论什么时候,当内核要为指定的kobject产生事件时,都要调用filter函数.如果filter返回0,则不产生事件.因此该函数给kset一个机会,用于决定是否向用户空间传递特定的事件.
使用该函数的一个例子是块设备子系统.在该子系统中,至少使用了3种类型的kobject,它们是磁盘,分区和请求队列.用户空间将向磁盘或者分区的添加产生响应,但通常响应请求队列的变化.因此filter()函数只允许为kobject产生磁盘和分区的事件.请看下面代码:
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kboj);
return( (ktype==&ktype_block)||(ktype==&ktype_part) );
}