kobject与kset

参考:http://blog.chinaunix.net/uid-25695950-id-4475182.html

           https://blog.csdn.net/lizuobin2/article/details/51511336

kobject、kset、ktype是设备模型中的基本机构。kobject基本结构体,kset是某相同kobject集合,ktype是类型。kobject对应sys虚拟文件系统下的目录,同理kset创建的目录下有更多子目录,attribute生产文件。kset和ktype可以看成是kobject的扩充,关系如下:

kobject与kset_第1张图片

kobject->parent与上级建立联系,构成树结构,kset->list 构成同级结构

核心结论:

    1、sys 目录下的层次结构依赖于 kobject.parent ,未指定parent时,默认使用 kobject.kset.kobject 作为 parent,如果都没有,就出现在 /sys 目录下。

    2、该 kobject 目录下的属性文件依赖于 kobject.ktype

结构体如下:

//kobject结构体
struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;// 上级目录
	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;
};

//kobject操作函数
struct kobj_type {
	void (*release)(struct kobject *kobj);
	struct sysfs_ops *sysfs_ops; // 操作函数集
	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); //写属性文件时调用该函数
};

//kset
struct kset {
	struct list_head list; //集合链表
	spinlock_t list_lock;
	struct kobject kobj; //集合自身有个kobject
	struct kset_uevent_ops *uevent_ops;//集合操作函数
};

//集合操作函数
struct kset_uevent_ops {
	int (*filter)(struct kset *kset, struct kobject *kobj);// 是否过滤,返回1不过滤
	const char *(*name)(struct kset *kset, struct kobject *kobj);// 返回名字
	int (*uevent)(struct kset *kset, struct kobject *kobj, 
		      struct kobj_uevent_env *env);// 上报,信息保存在 kobj_uevent_env 
};

一些函数都是创建kobject和kset。初始化其parant和函数操作集合。这里就不一一列出了。

例子:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
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);

kobject与kset_第2张图片

注意:通常定义一个kobject时 不定义默认属性。

kset结构包含一个kobject结构,在sys文件系统上表现出的也是一个目录。和kobject不同的是,kset的目录还可以包含目录,而kobject的目录只能包含属性文件。kset中还包含其子kobject的热插拔处理函数。

例子2:注册两个kset,一个kset_p,一个kset_c。kset_c位于kset_p里面,所以当kset_c加入kset_p时会产生相关热插拔信息

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
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, // filter,name,uevent都为函数指针
        .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到系统,会再初始化些kobject的信息
        ret=kset_register(&kset_p);
        if(ret){
            printk("register kset_p fail\n");
            return -1;
        }


        //设置kset_c的名字
        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,会自动将c parent指向p的kobject
        kset_c.kobj.kset = &kset_p;
        //注册kset_c到系统,会再初始化些kobject的信息
        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);

kobject与kset_第3张图片

看到上报了kset_c的创建信息

kobject与kset_第4张图片

kset_c的注销信息

 

二、增加非默认属性

struct attribute {
	const char		*name; //名字
	struct module		*owner;
	mode_t			mode; //读写模式
};

struct device_attribute { // 设备非默认属性
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

#define __ATTR(_name,_mode,_show,_store) { \
	.attr = {.name = __stringify(_name), .mode = _mode },	\
	.show	= _show,					\
	.store	= _store,					\
}

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) //定义了静态变量

调用该宏,定义了一个静态变量。例如:

static DEVICE_ATTR(mmc_inserted, S_IRUGO, gpio_show, NULL);
展开:
dev_attr_mmc_inserted =  { \
	.attr = {.name = __stringify(mmc_inserted), .mode = S_IRUGO},	\
	.show	= gpio_show,					\
	.store	= NULL,					\
}

调用sysfs_create_file 添加非默认属性
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
	BUG_ON(!kobj || !kobj->sd || !attr);

	return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);

}

还是如上静态变量,添加非默认属性,如下:
ret = sysfs_create_file(&mmcsd_device->kobj,
		      &dev_attr_mmc_inserted.attr); //dev_attr_mmc_inserted 宏定义生成

 

你可能感兴趣的:(linux内核)