参考: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->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结构包含一个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);
看到上报了kset_c的创建信息
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 宏定义生成