kobject和kset
首先介绍Sys文件系统:基于RAM的文件系统,把内核数据结构以及他们之间的属性和关系展示给用户,从linux2.6引入,sys同proc同类级别属于内存的文件系统,设备和总线驱动模型就在sysfs中,被加载到 /sys/目录下,子目录包括
1:Block:里面存放块设备的信息,系统中每个块设备在该目录下都会有一个子目录。
2:Bus:在Linux中注册的每一条总线在Bus目录下都会有一个子目录,如pci,i2c,并且在每个总线目录里面又包含两个子目录,有device和driver目录,device里存放挂载在该总线上的设备,driver包含注册到该总线的所有驱动。
3:Class:按照功能对设备进行分类。4:Device:所有的设备。5:Kernel:内核中的配置参数。Module:系统中所有模块的信息。6:FirmWare:系统中的固件。
7:Fs:系统中用到的文件系统。 8:Power:电源
下图反映sys中文件目录之间的关系,系统中一个设备按照不同的划分方法可能存在于三个不同目录下,比如鼠标在Class目录是输入设备,在Buses目录是USB设备,在Devices目录是所有设备中的一个,最终都指到一个设备,在系统中看到的是一个链接,
[aaa@localhost usbmon]$ ll usbmon0
lrwxrwxrwx. 1 root root 0 Feb 22 23:30 usbmon0 -> ../../devices/virtual/usbmon/usbmon0
usbmon0 这个设备有个软链接,往上链到Device,Buses里某个目录,
Kobject:实现了面向对象的机制,在机制中充当父类的角色,后面很多对象结构都包含kobject,这些对象就包含kobject的功能,每个kobject对象对应sysfs文件系统中的一个目录,作用:创建sys下的目录。
下面看怎么注册kobject
void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 初始化kobject结构
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...) 添加到系统
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
初始化kobject,并将其注册到Linux系统
void kobject_del(struct kobject *kobj) 删除kobject对象
Kobject中的ktype成员是一个指向kobj_type结构的指针,记录了kobject对象的一些属性。
struct kobj_type {
void (*release) (struct kobject *kobj) ;
struct sysfs_ops *sysfs_ops ;
struct attribute **default_attrs ;
};
struct attribute {
char *name ; //属性文件名
struct module *owner ; //拥有者
mode_t mode ; //属性保护位
}
Kobject的一个属性struct attribute就对应了kobject目录下的一个文件,name就是文件名。
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
由两个函数指针构成 show:第一、第二个参数都是内核传给我们的,第三个参数需要我们传给内核,当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中并返还给用户态。
store:当用户写的时候函数调用,存储用户传入的属性值,值从用户处来。
#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);
struct attribute test_attr = {
.name = "kobj_config",
.mode = S_IRWXUGO,
}; //属性名name是文件名kobj_config,mode是文件读写权限
static struct attribute *def_attrs[] = {
&test_attr,
NULL,
}; //指针数组,每个指针代表一个文件,这里只有一个文件test_attr
struct sysfs_ops obj_test_sysops =
{
.show = kobj_test_show,
.store = kobj_test_store,
}; //show和store是读写test_attr文件时调用
struct kobj_type ktype =
{
.release = obj_test_release,
.sysfs_ops=&obj_test_sysops,
.default_attrs=def_attrs,
}; //ktype赋值记录kobject属性和操作
void obj_test_release(struct kobject *kobject)
{
printk("eric_test: release .\n");
}
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;
} //打印信息,返回文件名字给用户
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;
}
struct kobject kobj;
static int kobj_test_init()
{
printk("kboject test init.\n");
kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");
//初始化并添加一个kobject,parent父目录为NULL,目录创建在sys目录
return 0;
}
static int kobj_test_exit()
{
printk("kobject test exit.\n");
kobject_del(&kobj);
return 0;
}
module_init(kobj_test_init);
module_exit(kobj_test_exit);
Kobject用来创建sys文件系统下的目录的,其他结构包含他其他结构也能在sys文件系统下创建目录。Kobject里有属性,属性对应文件,初始化注册kobject。
KSET
Kset是相同类型kobject的集合,对应的也是目录,是Kobject父母,可以包含目录,kobject不能包含目录,只能包含文件,如果想在目录里包含目录就用kset。
int kset_register(struct kset *k) 注册一个kset
void kset_unregister(struct kset *k) 注销kset
Kset在使用时会引起热插拔事件,比如添加kset到系统,移动kobject,因为sys目录里的文件发生变化,会把信息从内核空间通知到用户空间,应用程序就会处理。
热插拔事件由一个结构(包含三个指针)处理,
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
当kset的子目录kobject或kset状态发生变化时(如被加入,移动),上面三个函数将被调用。
Filter:可以决定当事件产生时,是否可以把这个事件传给用户空间,返回1函数执行。
Name:用于将字符串传给用户空间的热插拔处理程序。
Uevent:将用户空间需要的参数添加到环境变量(告诉用户触发事件的类型)。
#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>
struct kset kset_p;
struct kset kset_c;
int kset_filter(struct kset *kset, struct kobject *kobj)
{
printk("Filter: kobj %s.\n",kobj->name);
return 1;
} //首先调用,决定剩下两个函数要不要,过滤作用
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;
} //kset名
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;
} //把事件告诉用户空间
struct kset_uevent_ops uevent_ops =
{
.filter = kset_filter,
.name = kset_name,
.uevent = kset_uevent,
};
int kset_test_init()
{
printk("kset test init.\n");
kobject_set_name(&kset_p.kobj,"kset_p"); //第一个kset_p
kset_p.uevent_ops = &uevent_ops;
//处理热插拔事件,当kset下的目录发生变化时
kset_register(&kset_p); //系统中添加一个目录
kobject_set_name(&kset_c.kobj,"kset_c"); //
kset_c.kobj.kset = &kset_p; // kset_c.kobject指明kset_c目录属性,.kset用于指明父目录
//kset_c是子目录,kset_p是父目录,触发热插拔函数
kset_register(&kset_c);
return 0;
}
int kset_test_exit()
{
printk("kset test exit.\n");
kset_unregister(&kset_p);
kset_unregister(&kset_c);
return 0;
}
module_init(kset_test_init);
module_exit(kset_test_exit);
执行效果