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消息。

浙公网安备 33010602011771号