kobject kset
sysfs文件系统
sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.”
--- documentation/filesystems/sysfs.txt
sysfs类似proc文件系统,把连接在系统上的设备和总线组织成分级的文件,使用户在用户空间可以访问
ls /sys/ block/ bus/ class/ devices/ firmware/ kernel/ module/ power/
Block目录:包含所有的块设备
Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
Bus目录:包含系统中所有的总线类型
ls /sys/bus
i2c/ ide/ pci/ pci express/ platform/ pnp/ scsi/ serio/ usb/
总线包含两个子目录
drivers:包含注册到该总线的所有驱动
devices:包含在整个系统中发现的属于该总线的类型设备
Class目录(按照功能分类):系统中的设备类型(如网卡设备,声卡设备等)
decives: 所有的系统设备
kernel:内核配置参数
Module:系统中所有的模块信息
Firmweare:系统中固件
Fs:描述系统中的文件系统
power:系统中电源选项
Drivers目录:包括内核中所有已注册的设备驱动程序
一个设备在class下是一个鼠标,在bus下是一个usb,最终软连接到devices下
kobject
kobject实现了面向对象的管理机制,在内核中每个kobject对象对应sysfs文件系统中的一个目录,通过struct kobject表示,使所有设备在底层具有统一的接口
struct kobject { char *name; 指向设备名称的指针 char name[KOBJ NAME LEN]; 设备名称 struct kref kref; 对象引用计数 struct list_head entry; 挂接到所在kset中去的单元 struct kobject * parent; 指向父对象的指针 struct kset * kset; 所属kset的指针 struct kobj_type * ktype; 指向其对象类型描述符的指针 struct dentry * dentry; sysfs文件系统中与该对象对应的文件节点路径指针 };
处理函数
void kobject_init(struct kobject * kobj); //kobject初始化函数。
int kobject_add(struct kobject * kobj);
//将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,
//在其 parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。
int kobject_register(struct kobject * kobj); /*kobject注册函数。通过调用kobject init()初始化kobj,再调用kobject_add()完成该内核对象的注册 */
void kobject_del(struct kobject * kobj);//从Linux设备层次(hierarchy)中删除kobj对象。
void kobject_unregister(struct kobject * kobj);//kobject注销函数。与kobject register()相反,它首先调用kobject del从设备层次中删除该对象,再调用kobject put()减少该对象的引用计数,如果引用计数降为0,则释放kobject对象
struct kobj_type { void (*release)(struct kobject *); struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; };
ktype是一个指向kobj_type结构的指针,记录一些kobject的属性。Kobj_type数据结构包含三个域:一个release方法用于释放kobject占用的资源;一个sysfs_ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两个函数store()和show()。当用户态读取属性 时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。
struct attribute { char * name; //属性文件名 struct module * owner; mode_t mode; //属性保护位 }
attribute, 属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件名就是name。文件读写的方法对应于kobj type中的sysfs ops。
kset
kset是相同类型的kobject的集合。kset最重要的是建立上层(sub-system)和下层的 (kobject)的关联性。kobject 也会利用它了分辨自已是属于那一個类型,然後在/sys 下建立正确的目录位置。而kset 的优先权比较高,kobject会利用自已的*kset 找到自已所属的kset,並把*ktype 指定成該kset下的ktype,除非沒有定义kset,才会用ktype來建立关系。Kobject通过kset组织成层次化的结构,kset是具有相 同类型的kobject的集合,在内核中用kset数据结构表示,定义为:
struct kset { struct subsystem * subsys; //所在的subsystem的指针 struct kobj type * ktype; //指向该kset对象类型描述符的指针 struct list head list; //用于连接该kset中所有kobject的链表头 struct kobject kobj; //嵌入的kobject struct kset hotplug ops * hotplug ops;// 指向热插拔操作表的指针 }
热插拔事件
当系统配置发生变化时,如kset添加到系统,移动kobject,一个通知会从内核空间发送到用户空间,就是热插拔事件,热插拔事件会导致用户空间中的相应处理程序(udev,mdev)被调用,这些处理程序会加载驱动,创建节点相应热插拔。
操作合集
struct kset_uevent_ops{ int (*filter)(struct kset *kset, struct kobject *kobj);//决定是否将事件传递到用户空间中 const char *(*name)(struct kset *kset, struct kobject *kobj);//用于将字符串传递给用户空间的热插拔处理程序 int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);//将用户空间需要的参数添加到环境变量中 }
当kset所管理的kobject和kset状态发生变化时,被加入,移动,这三个函数将会被调用
实例分析
#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("GPL"); struct kset *kset_p; struct kset kset_c; /* 函数声明 */ void obj_test_release(struct kobject *); ssize_t kobj_test_show(struct kobject *,struct attribute *,char *); ssize_t kobj_test_store(struct kobject *,struct attribute *,const char *,size_t); static struct attribute test_attr = { .name = "kobj_config", .mode = S_IRWXUGO, }; static struct attribute *def_attrs[] = { &test_attr, NULL, }; static struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; static struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops = &obj_test_sysops, .default_attrs = def_attrs, }; void obj_test_release(struct kobject *kobject) { printk("[kobj_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 size) { printk("Have store -->\n"); printk("write: %s.\n",buf); return size; } static int kset_filter(struct kset *kset,struct kobject *kobj) { printk("Filter: kobj %s.\n",kobj->name); return 1; } static 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; } static 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; } static struct kset_uevent_ops uevent_ops = { .filter = kset_filter, .name = kset_name, .uevent = kset_uevent, }; static int __init kset_test_init(void) { int ret = 0; printk("kset test init!\n"); /* 创建并注册 kset_p */ kset_p = kset_create_and_add("kset_p",&uevent_ops,NULL); /* 添加 kset_c 到 kset_p */ kobject_set_name(&kset_c.kobj,"kset_c"); kset_c.kobj.kset = kset_p; /* 对于较新版本的内核,在注册 kset 之前,需要 * 填充 kset.kobj 的 ktype 成员,否则注册不会成功 */ kset_c.kobj.ktype = &ktype; ret = kset_register(&kset_c); if(ret) kset_unregister(kset_p); return 0; } static void __exit 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);