bloak目录,存放块设备信息。每个块设备都会对应bloak目录下的一个子目录记录设备的相关信息。
bus 每条总线在该文件夹下对应一个子目录,如i2c目录,子目录如i2c下又对应两个子目录 Devices目录和Drivers目录 Drivers目录:包括注册到该总线的所有的设备驱动 Devices目录:对应属于该总线类型的设备
class目录:按照设备功能进行分类,如net子目录包含了网络接口
devices:所有设备
kernel:内核的配置参数
module:系统中所有模块的信息
firmware:系统中的固件
FS:系统中的文件系统
power:系统中电源选项
所有的设备都位于devices目录中,别的目录下设备会通过链接连接到device目录下
structk object { constchar *name; structlist_head entry; structkobject *parent;//指向父对象 structkset *kset; structkobj_type *ktype; structsysfs_dirent *sd; structkref kref;//对象引用计数 unsignedint state_initialized:1; unsignedint state_in_sysfs:1; unsignedint state_add_uevent_sent:1; unsignedint state_remove_uevent_sent:1; };
注册kobject步骤 函数
kobject_init(struct kobject * kobj) 初始化 kobject_add(struct kobject * kobj) 添加对象 kobject_init_and_add(struct kobject *kobj, struct kobj_type*ktype,struct kobject *parent, const char *fmt, ...) 相当于完成上面两步。struct kobject *parent表示目录,如果为空创建在sysfs目录下,*fmt表示设备名字 kobject_del() 删除对象 kobject_get()将kobject对象计数加1 kobject_put() kobject对象计数减1
文件属性
Kobject的ktype成员是一个指向kobj_type结构的指针, 该结构中记录了kobject对象的一些属性以及一些操作。 struct kobj_type { void(*release)(struct kobject *kobj); struct sysfs_ops *sysfs_ops; //读写 属性文件 struct attribute **default_attrs; //属性文件 }; release:用于释放kobject占用的资源,当kobject的引用计数为0时被调用。
属性结构体
struct attribute { char *name; /*属性文件名*/ mode_tmode; /*属性的保护位*/ //读写属性 }; 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_AUTHOR("cicue"); MODULE_LICENSE("Dual BSD/GPL"); /*声明release、show、store函数*/ 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); /*对应于kobject的目录下的一个文件,Name成员就是文件名*/ struct attribute test_attr = { .name = "kobj_config", .mode = S_IRWXUGO, }; static struct attribute *def_attrs[] = { &test_attr, NULL, }; /kobject对象的操作 struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; /*定义kobject对象的一些属性及对应的操作*/ struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops=&obj_test_sysops, .default_attrs=def_attrs, }; /*release方法释放该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;//声明kobject对象 static int kobj_test_init(void) { printk("kboject test init.\n"); kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");//初始化kobject对象kobj,并将其注册到linux系统 return 0; } static void kobj_test_exit(void) { printk("kobject test exit.\n"); kobject_del(&kobj); } module_init(kobj_test_init); module_exit(kobj_test_exit);
运行结果是创建目录 /sys/kobject_test/kobj_config
热插拔事件,当系统配置发生变化时,如添加kset到系统,移动kobject,一个通知会从内核空间移动到用户空间,这既是热插拔,热插拔会导致用户空间相应的处理程序被调用,这些程序通过调用设备驱动,创建设备节点来响应热插拔。
struct kset { struct list_head list; //连接该kset中所有kobject的链表头 spinlock_t list_lock; struct kobject kobj; //内嵌的kobject struct kset_uevent_ops *uevent_ops; //处理热插拔事件的操作集合 }
子目录中还有子目录是kset,如果子目录只有文件是kobject.
1)int kset_register(struct kset *kset) 在内核中注册一个kset 2)void kset_unregister(struct kset *kset) 从内核中注销一个kset
功能结构
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); } 三个函数功能 1)filter:决定是否将事件传递到用户空间。如果filter返回0,将不传递事件。(例: uevent_filter) 2)name:用于将字符串传递给用户空间的热插拔处理程序。 3)uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)
当该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_AUTHOR("cicue"); 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);
运行效果为 /sys目录下 创建了 /kset_p/kset_c/kobj_config
1)地址总线注册bus_register(struct bus_type *bus);
若成功,新的总线将被添加进系统,并可在sysfs 的 /sys/bus 下看到。
2)总线的删除使用:void bus_unregister(struct bus_type *bus)
总线方法
1)int (*match)(struct device * dev, struct device_driver * drv)
当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。
2)int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。
总线属性由结构bus_attribute 描述,定义如下:
struct bus_attribute {
struct attribute
attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char *
buf, size_t count);
}
1)int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
创建属性
2)void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
删除属性
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> MODULE_AUTHOR("cicue"); MODULE_LICENSE("Dual BSD/GPL"); static char *Version = "$Revision: 1.0 $"; /*当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。*/ static int my_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev->kobj.name, driver->name, strlen(driver->name)); } /*声明总线*/ struct bus_type my_bus_type = { .name = "my_bus", //总线名字 .match = my_match, //总线match函数指针 }; static ssize_t show_bus_version(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); } /*内核代码中如此定义:#define BUS_ATTR(_name, _mode, _show, _store) \ struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store), 它将bus_attr_作为给定的name的前缀来创建总线的真正名称。对应下面的是bus_attr_version*/ static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); /*模块加载函数*/ static int __init my_bus_init(void) { int ret; /*注册总线*/ ret = bus_register(&my_bus_type); if (ret) return ret; /*创建属性文件*/ if (bus_create_file(&my_bus_type, &bus_attr_version)) printk(KERN_NOTICE "Fail to create version attribute!\n"); return ret; } /*模块卸载函数*/ static void my_bus_exit(void) { bus_unregister(&my_bus_type); } module_init(my_bus_init); module_exit(my_bus_exit);
运行结果:创建 /sys/bus /my_bus/ 里面包含下面的
devices drivers 目录
drivers_autoprobe uevent drivers_probe version 文件