linux 设备模型(二)
如果将linux设备模型比喻一座大厦,那么kobject 和 kset就是构成这座大厦内部的钢筋,由若干他们构成了大厦的内在表现形式,设备驱动模型中bus,device和driver 已经是大厦想外界展示的那部分,属于外在表现形式了。在我们写驱动的时候与我们打交道的是bus,device和driver,基本上不用去管更底层的东西,那些内核都帮我们做好了。但是,如果要灵活的使用linux设备模型,应该了解内核实现设备模型的流程。在这个过程中,自己也没有细入分析各个步骤,因为在我看来linux设备模型是一个复杂的系统,设计的数据结构也不是一时能够弄懂,在学习的阶段,实在是没有必要花太多时间在这上面,首先,从总体上对linux设备模型的实现及涉及的数据结构有个整体把握,就像一位前辈所说学习的工程关键是抓住要点。(事实上,在阅读linux内核代码时也不应该是一句句的阅读)
在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等。引用计数本质上就是利用kref实现的。
另外,Linux设备模型还有一个重要的数据结构kset。Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录,但它和kobject的不同之处在于kset可以看作是一个容器,如果你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct list_head。
一个kobject对应sysfs里的一个目录,Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录。通过编写两个程序,可以对kobject 和 kset有个感性的人是。代码如下:
kobject.c
#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> 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(void) { printk(" kobject test init.\n"); kobject_init(& kobj); kobj.ktype= &ktype; snprintf(kobj.name, KOBJ_NAME_LEN, "%s", "new_obj"); kobj.parent = NULL; kobject_add(& kobj); 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); MODULE_AUTHOR("lsx"); MODULE_LICENSE("GPL");
kset.c
#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; } int kset_uevent(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size) { int i = 0; printk("uevent: kobj %s.\n",kobj->name); while( i < num_envp){ printk("%s.\n",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); 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); MODULE_LICENSE("GPL");
测试结果:
# insmod /mnt/kobject.ko kobject test init. # ls block class firmware kernel new_obj bus devices fs module power # cd new_obj/ # ls kobj_config # cat kobj_config have show. attrname:kobj_config. kobj_config # insmod /mnt/kset.ko kset test init. # ls block class firmware kernel module bus devices fs kset_p power # cd kset_p/ # ls kset_c # ls kset_c # cd kset_c/
总结:kset对象与单个kobject对象不一样的地方在于将一个kset对象系统注册,如果linux内核编译时启动了CONFIG_HOTPLUG,那么需要将这一事件通知用户空间,这个过程由kobject_event完成。如果一个kobject对象不属于kset,那么孤立的kobject对象无法通过uevent机制向用户空间发送uevent消息。