linux设备驱动模型(kobject与kset)
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。
kobject
1.sysfs文件系统
1.1 sysfs文件系统与内核结构的关系
linux内核中的结构 | sysfs中的结构 |
kobject (内核对象) | 目录 |
kobj_type (属性) | 属性文件 |
对象之间的关系 | 符号链接 |
1.2 sysfs文件系统的目录结构
block:所有块设备
devices:系统所有设备(块设备特殊),对应struct device的层次结构
bus:系统中所有总线类型(指总线类型而不是总线设备,总线设备在devices下),bus的每个子目录都包含
--devices:包含到devices目录中设备的软链接
--drivers:与bus类型匹配的驱动程序
class:系统中设备类型(如声卡、网卡、显卡等)
fs:一些文件系统,具体可参考filesystems /fuse.txt中例子
dev:包含2个子目录
--char:字符设备链接,链接到devices目录,以<major>:<minor>命名
--block:块设备链接
2.设备驱动模型的核心数据结构
2.1 kobject结构体
name 将显示在sysfs文件系统中,作为一个目录的名字;
struct kobj_type *ktype 代表kobject的属性,对于sysfs中的普通文件读写操作都是kobjetc->ktype->sysfs_ops指针来完成的,即对default_attrs数组的操作。
kobject始终是sysfs文件系统中的一个目录而不是文件。
2.2设备属性kobj_type
下图是kobject与kobj_type的关系:
kobj_type结构体的定义如下:
struct kobj_type { void (*release)(struct kobject *kobj);//释放kobject和其他占用资源的函数 const struct sysfs_ops *sysfs_ops;//操作下一个数组的方法 struct attribute **default_attrs;//属性数组 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); };
【1】attribute结构体如下:
struct attribute { const char *name;//属性名字 struct module *owner;// mode_t mode;//属性的读写权限 #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key *key; struct lock_class_key skey; #endif };
【2】sysfs_ops 属性操作结构体:
struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *);//读属性 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);//写属性 };
【3】release()函数
当kobject的引用计数为0时,系统自动会调用自定义的release()来释放kobject对象。
e.g.
3.实例:
#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("David Xie"); 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, }; static struct attribute *def_attrs[] = { &test_attr, NULL, }; struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; 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("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"); 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);
kset
kset是具有相同类型的kobject的集合,在sysfs中体现成一个目录;kobject不能包含目录,而kset可以包含目录。kobject通过kset组织成层次化的结构,kset将一系列相同类型的kobject使用(双向)链表连接起来,可以这样 认为,kset充当链表头作用,kset内部内嵌了一个kobject结构。
- #include <linux/kobject.h>
- struct kset {
- struct list_head list; /* 用于连接kset中所有kobject的链表头 */
- spinlock_t list_lock; /* 扫描kobject组成的链表时使用的锁 */
- struct kobject kobj; /* 嵌入的kobject */
- const struct kset_uevent_ops *uevent_ops; /* kset的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> MODULE_AUTHOR("David Xie"); MODULE_LICENSE("Dual BSD/GPL"); 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; } 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.uevent_ops = &uevent_ops; kset_register(&kset_p); printk("kset_p finish\n"); /*下面会调用热插拔函数*/ kobject_set_name(&kset_c.kobj,"kset_c"); kset_c.kobj.kset = &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);
热插拔事件kset_uevent_ops
在Linux系统中,当系统配置发生变化时,如:添加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状态发生变化时(如被加入,移动),这三个函数将被调用。
kobject与kset的关系
参考:http://blog.csdn.net/xiahouzuoxin/article/details/8943863