kobject与kset的介绍,在ldd3中已经介绍的很详细了,没有了解过的,可以去看看第十四章设备模型这一章,这里只简单介绍一下kobject和kset,并提供一个可运行的示例程序,用做备忘!
示例程序运行kernel版本为3.0.36,示例程序是国嵌的,修改使之在3.0.36内核版本可运行,并消除编译时的警告信息
kobject实现了基本的面相对象管理机制,是构成Linux内核设备模型的核心结构,它与sys紧密相连,在内核中注册的每个kobject对应sys文件系统中一个目录。kobject结构定义如下:
struct kobject {
const char *name; //名字
struct list_head entry;
struct kobject *parent; //父kobject
struct kset *kset;
struct kobj_type *ktype; //kobject类型
struct sysfs_dirent *sd;
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;
};
struct kobj_type结构定义如下:
struct kobj_type {
void (*release)(struct kobject *kobj); //引用计数为0时,调用该函数释放kobject所占用的资源
const struct sysfs_ops *sysfs_ops; //属性文件的操作函数
struct attribute **default_attrs; //属性文件,对应sys文件系统中的一个文件
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *); //读如上属性文件时调用该函数
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); //写如上属性文件时调用该函数
};
直接贴上示例程序,该模块只是简单注册一个kobject,kobject中包含一个名为kobj_config的属性文件
-
#include <linux/device.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/string.h>
-
#include <linux/sysfs.h>
-
#include <linux/stat.h>
-
-
MODULE_LICENSE("Dual BSD/GPL");
-
-
void obj_test_release(struct kobject *kobject);
-
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);
-
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
-
-
-
//定义的一个属性文件,名字为kobj_config,权限为可读可写
-
struct attribute test_attr = {
-
.name = "kobj_config",
-
.mode = S_IRWXUGO,
-
};
-
-
static struct attribute *def_attrs[] = {
-
&test_attr,
-
NULL,
-
};
-
-
-
struct sysfs_ops obj_test_sysops =
-
{
-
.show = kobj_test_show, //当对属性文件kobj_config进行读时,执行此函数
-
.store = kobj_test_store,//当对属性文件kobj_config进行写时,执行此函数
-
};
-
//定义一个kobj_type,描述要注册的kobject的类型信息,如属性和释放操作
-
struct kobj_type ktype =
-
{
-
.release = obj_test_release, //kobject释放函数
-
.sysfs_ops=&obj_test_sysops, //属性文件操作函数
-
.default_attrs=def_attrs, //定义的默认属性文件数组
-
};
-
//当kobj的应用计数为0时,用于释放kobj的资源,这里只是执行了一条打印信息
-
void obj_test_release(struct kobject *kobject)
-
{
-
printk("eric_test: release .\n");
-
}
-
-
//当对属性文件kobj_config进行读时,执行此函数
-
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf)
-
{
-
printk("have show.\n");
-
printk("attrname:%s.\n", attr->name);
-
sprintf(buf,"%s\n",attr->name);
-
return strlen(attr->name)+2;
-
}
-
//当对属性文件kobj_config进行写时,执行此函数
-
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
-
{
-
printk("havestore\n");
-
printk("write: %s\n",buf);
-
return count;
-
}
-
//定义一个kobj,在模块加载函数中注册
-
struct kobject kobj;
-
//模块加载函数
-
static int kobj_test_init(void)
-
{
-
int ret;
-
printk("kboject test init111.\n");
-
//初始化并添加一个kobject
-
//&kobj==添加的kobject;&ktype==kobject的ktype结构;NULL==该注册的kobject的父kobject,如果为NULL,这表示注册到sys最顶层目录;"kobject_test"==kobject的名字;
-
ret=kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");
-
if(ret)
-
return -1;
-
return 0;
-
}
-
-
//模块注销时执行此函数,删除注册的kobject
-
static void kobj_test_exit(void)
-
{
-
printk("kobject test exit.\n");
-
kobject_del(&kobj);
-
}
-
-
module_init(kobj_test_init);
-
module_exit(kobj_test_exit);
编译完成之后,看看加载之后的效果,执行insmod kobject.ko
操作结果我直接截图了
如我们预期的一样,加载kobject.ko成功之后,在sys/目录下多了一个kobject_test目录,该目录下有一个kobj_config的属性文件。
读kobj_config文件时,程序中的kobj_test_show会得到执行,对kobj_config文件进行写操作时,kobj_test_store函数会得到执行
--------------------------------------------------------------------------------------------------------------------------
kset是嵌入相同类型结构的kobject的集合,kset结构包含一个kobject结构,在sys文件系统上表现出的也是一个目录。和kobject不同的是,kset的目录还可以包含目录,而kobject的目录只能包含属性文件。kset中还包含其子kobject的热插拔处理函数。
kset 结构定义如下:
struct kset {
struct list_head list; //链接该kset中所有的kobject的链表头
spinlock_t list_lock;
struct kobject kobj; //内嵌的kobject
const struct kset_uevent_ops *uevent_ops; //处理热插拔事件的操作集合
};
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj); //决定是否将产生事件传递到用户空间,如果返回0,将不传递事件
const char *(* const name)(struct kset *kset, struct kobject *kobj);// 用于将字符串传递给用户空间的热插拔处理程序
int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); //通过环境变量传递热插拔脚本需要的信息,环境变量存在kobj_uevent_env中
};
下面还是提供一个简单的示例程序,注册两个kset,一个kset_p,一个kset_c,kset_c位于kset_p里面,所以当kset_c加入kset_p时会产生相关热插拔信息
-
#include <linux/device.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/string.h>
-
#include <linux/sysfs.h>
-
#include <linux/stat.h>
-
#include <linux/kobject.h>
-
-
MODULE_LICENSE("Dual BSD/GPL");
-
-
struct kset kset_p;
-
struct kset kset_c;
-
void kset_p_release(struct kobject *kobject);
-
void kset_c_release(struct kobject *kobject);
-
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);
-
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
-
-
-
struct sysfs_ops kset_p_sysops =
-
{
-
.show = kobj_test_show,
-
.store = kobj_test_store,
-
};
-
struct sysfs_ops kset_c_sysops =
-
{
-
.show = kobj_test_show,
-
.store = kobj_test_store,
-
};
-
//kset_p内嵌kobject的类型信息,这里没有做添加具体属性
-
struct kobj_type ktype_p =
-
{
-
.release = kset_p_release,
-
.sysfs_ops=&kset_p_sysops,
-
};
-
//kset_c内嵌kobject的类型信息,这里没有做添加具体属性
-
struct kobj_type ktype_c =
-
{
-
.release = kset_c_release,
-
.sysfs_ops=&kset_c_sysops,
-
};
-
-
void kset_p_release(struct kobject *kobject)
-
{
-
printk("eric_test: kset_p_release .\n");
-
}
-
void kset_c_release(struct kobject *kobject)
-
{
-
printk("eric_test: kset_c_release .\n");
-
}
-
-
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf)
-
{
-
return 0;
-
}
-
-
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
-
{
-
return 0;
-
}
-
//决定是否将产生事件传递到用户空间,如果返回0,将不传递事件
-
//此处永远返回1,表示将产生事件传递到用户空间
-
int kset_filter(struct kset *kset, struct kobject *kobj)
-
{
-
printk("Filter: kobj %s.\n",kobj->name);
-
return 1;
-
}
-
-
// 用于将字符串传递给用户空间的热插拔处理程序,传递的字符串存入buf中
-
const char *kset_name(struct kset *kset, struct kobject *kobj)
-
{
-
static char buf[20];
-
printk("Name: kobj %s.\n",kobj->name);
-
sprintf(buf,"%s","kset_name");
-
return buf;
-
}
-
//通过环境变量传递热插拔脚本需要的信息,环境变量存在kobj_uevent_env中
-
//此处只是将环境变量信息打印出来,并没有添加新的信息
-
int kset_uevent(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env)
-
{
-
int i = 0;
-
printk("uevent: kobj %s.\n",kobj->name);
-
-
while( i < env->envp_idx){
-
printk("%s.\n",env->envp[i]);
-
i++;
-
}
-
-
return 0;
-
}
-
-
//kset_p热插拔处理程序,这里当注册kset_c时,即将kset_c加入kset_p时,相关函数会执行
-
struct kset_uevent_ops uevent_ops =
-
{
-
.filter = kset_filter,
-
.name = kset_name,
-
.uevent = kset_uevent,
-
};
-
-
//模块加载函数
-
int kset_test_init(void)
-
{
-
int ret;
-
-
printk("kset test init1.\n");
-
//设置kset_p的名字
-
kobject_set_name(&kset_p.kobj,"kset_p");
-
//设置kset_p内嵌Kobject的ktype类型,我在3.0.36的代码里运行,必现要加这个,不然会出现内核异常
-
kset_p.kobj.ktype=&ktype_p;
-
//设置热插拔处理程序
-
kset_p.uevent_ops = &uevent_ops;
-
//注册kset_p到系统
-
ret=kset_register(&kset_p);
-
if(ret){
-
printk("register kset_p fail\n");
-
return -1;
-
}
-
//设置kset_次的名字
-
kobject_set_name(&kset_c.kobj,"kset_c");
-
//设置kset_c内嵌Kobject的ktype类型,我在3.0.36的代码里运行,必现要加这个,不然会出现内核异常
-
kset_c.kobj.ktype=&ktype_c;
-
//设置kset_c内嵌的kobject结构的kset指针执行kset_p,就相当于设置kset_c的父目录为kset_p
-
kset_c.kobj.kset = &kset_p;
-
//注册kset_c到系统
-
ret=kset_register(&kset_c);
-
if(ret){
-
printk("register kset_c fail\n");
-
return -1;
-
}
-
return 0;
-
}
-
-
void kset_test_exit(void)
-
{
-
printk("kset test exit.\n");
-
kset_unregister(&kset_p);
-
kset_unregister(&kset_c);
-
}
-
-
module_init(kset_test_init);
-
module_exit(kset_test_exit);
编译完成之后,看加载的执行效果
加载程序时相应的热插拔程序得到了执行,同时sys目录下也增加了相应的文件夹
删除kset模块
删除之后的热插拔信息,并没有打印出来,当我再次加载kset时,上传删除kset的热插拔打印信息才打印出来,不解中
最后再贴上编译kobject.c和kset.c的Makefile文件,如要使用该Makefile,请自行修改编译工具链,编译内核代码地址,和编译文件
-
ifneq ($(KERNELRELEASE),)
-
-
obj-m := kset.o //要编译的文件
-
-
else
-
-
KDIR := /home/jason/h18/pizza/kernel //运行系统的kernl代码目录
-
all:
-
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-eabi- //编译链
-
clean:
-
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
-
-
endif
阅读(1618) | 评论(0) | 转发(0) |