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

posted @ 2013-03-28 20:45  lsx_007  阅读(219)  评论(0编辑  收藏  举报