Chinaunix首页 | 论坛 | 博客
  • 博客访问: 845
  • 博文数量: 1
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 15
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-07 14:57
文章分类

全部博文(1)

文章存档

2013年(1)

我的朋友

分类: Android平台

2013-04-08 13:44:21

一、kobject的定义:
kobject是Linux2.6引入的设备管理机制,在内核中由struct kobject结构表示,这个结构使所有设备在底层都具有统一的接口.kobject提供了基本的对象管理能力,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密联系,每个在内核中注册kobject对象都对应与sysfs文件系统中的一个目录;kobject--->sysfs.dir;其结构定义为:
struct kobject
{
  const char*        k_name;              //指向设备名称的指针
  char               name[KOBJ_NAME_LEN]; //设备名称
  struct kref        kref;                //内核对象的引用计数
  struct list_head   entry;               //挂接到当前内核对象所在kset中的单元
  struct kobject*    parent;              //指向父对象的指针
  struct kset*       kset;                //内核对象所属kset的指针
  struct kobj_type*  ktype;               //指向内核对象类型描述符的指针
  struct dentry*     dentry;              //sysfs文件系统中与该内核对象对应的文件节点路径的指针
  wait_queue_head_t  poll;                //IO等待队列;
};
二、kobject相关函数:
1、void kobject_init(struct kobject* kobj);
   该函数用于初始化kobject对象,它设置kobject对象的引用计数为1,entry字段指向自身,其所属kset对象的引用计数加1;
2、void kobject_cleanup(struct kobject* kobj);
   void kobject_release(struct kref* ref);
   这两个函数用于清除kobject对象,当其引用计数为0时,释放对象所占用的资源;
3、int kobject_set_name(struct kobject* kobj, const char* format, ...);
   该函数用于设置指定kobject对象的名称;
4、const char* kobject_name(const struct kobject* kobj);
   该函数用于返回指定kobject的名称;
5、int kobject_rename(struct kobject* kobj, const char* new_name);
   该函数用于为指定kobject对象重命名;
6、struct kobject* kobject_get(struct kobject* kobj);
   该函数用于将kobject对象的引用计数加1,相当于申请了一个kobject对象资源,同时返回该kobject对象的指针;
7、void kobject_put(struct kobject* kobj);
   该函数用于将kobject对象的引用计数减1,相当于释放了一个kobject对象资源;当引用计数为0时,则调用kobject_release()释放该kobject对象的资源;
8、int kobject_add(struct kobject* kobj);
   该函数用于注册kobject对象,即:加入到Linux的设备层次中,它会挂接该kobject对象到kset的list链中,增加父目录各级kobject对象的引用计数,在其parent字段指向的目录下创建对应的文件节点,并启动该类型kobject对象的hotplug()函数;
9、void kobject_del(struct kobject* kobj);
   该函数与kobject_add()相反,用于注销kobject对象,即:中止该kobject对象的hotplug()函数,从Linux的设备层次中删除该kobject对象,删除该kobject对象在sysfs文件系统中对应的文件节点;
10、int kobject_register(struct kobject* obj);
    该函数用于注册kobject对象,它首先会调用kobject_init()初始化kobj,然后再调用kobject_add()完成该内核对象的添加;
11、void kobject_unregister(struct kobject* kobj);
    该函数与kobject_register()相反,用于注销kobject对象,它首先调用kobject_del()从Linux的设备层次中删除kobject对象,再调用kobject_put()减少该kobject对象的引用计数,当引用计数为0时,则释放该kobject对象的资源;
12、struct kobject* kobject_add_dir(struct kobject*, const char* path);
    该函数用于在sysfs文件系统中为该kobject对象创建对应的目录;
13、char* kobject_get_path(struct kobject* kobj);
    该函数用于返回该kobject对象在sysfs文件系统中的对应目录路径;
三、kobject的行为:
typedef int __bitwise kobject_action_t;
enum kobject_action
{
  KOBJ_ADD     = (__force kobject_action_t) 0x01, //exclusive to core
  KOBJ_REMOVE  = (__force kobject_action_t) 0x02, //exclusive to core
  KOBJ_CHANGE  = (__force kobject_action_t) 0x03, //device state change
  KOBJ_MOUNT   = (__force kobject_action_t) 0x04, //mount event for block devices (broken)
  KOBJ_UMOUNT  = (__force kobject_action_t) 0x05, //umount event for block devices (broken)
  KOBJ_OFFLINE = (__force kobject_action_t) 0x06, //device offline
  KOBJ_ONLINE  = (__force kobject_action_t) 0x07, //device online
};
该枚举类型用于定义kobject对象的状态更新消息码,也就是热插拔事件码;
备注:struct kobject结构定义于文件include/linux/kobject.h

下面转自:http://blog.chinaunix.net/u1/57901/showart_1803248.html
在LINUX中最让人不解的大概就是/sys下面的内容了
下面首先让我们来创建一个简单的platform设备,并从这个设备的视角进行深入,在此篇文章的深入过程中,我们只看kobeject的模型我所使用的内核版本号为2.6.26,操作系统的内核版本号为2.6.27-7,暂未发现2.6.27-7与2.6.26的重大不同
首先写一个简单的模块
#include
#include
#include
static int __init test_probe(struct platform_device *pdev)
{
        int err = 0;
        return err;
}
static int test_remove(struct platform_device *pdev)
{
        return 0;
}
static struct platform_device test_device = {
        .name = "test_ts",
        .id = -1,
};
static struct platform_driver test_driver = {
        .probe                = test_probe,
        .remove                = test_remove,
        .driver                = {
                .name        = "test_ts",
                .owner        = THIS_MODULE,
        },
};
static int __devinit test_init(void)
{
        platform_device_register(&test_device);        
        return platform_driver_register(&test_driver);
}
static void __exit test_exit(void)
{
        platform_device_unregister(&test_device);
        platform_driver_unregister(&test_driver);
}
module_init(test_init);
module_exit(test_exit);
MODULE_AUTHOR("zwolf");
MODULE_DESCRIPTION("Module test");
MODULE_LICENSE("GPL");
MODULE_ALIAS("test");
接下来是makefile
#Makefile
obj-m:=test.o
KDIR:=/lib/modules/2.6.27-7-generic/build
PWD:=$(shell pwd)
default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules
KDIR中的目录请改为各位实际运行中的内核目录make之后进行模块的加载 sudo insmod ./test.ko
现在到sys目录中查看我们的设备是否已经加载上了
首先是/sys/bus/platform/devices/在devices下,每一个连接文件都代表了一个设备ls可看见test_ts,进入test_ts,ls可发现driver这个链接文件,ls-l查看,发现这个文件是连到/sys/bus/platform/drivers/test_ts的
这里需要说明的是连接的含义,并不是driver驱动存在于test_ts这个设备中,而是test_ts使用的驱动为/sys/bus/platform/drivers/test_ts
现在换到/sys/bus/platform/drivers这个目录下
ls查看会发现这里的文件都为目录,而非连接文件,说明这是驱动真正放置的位置
现在进入test_ts目录,然后ls,发现有一个test_ts的连接文件,ls –l查看可发现该文件连接到/sys/devices/platform/test_ts下
回到/sys/bus/platform/devices/下ls –l也会发现test_ts连接到/sys/devices/platform/test_ts
为什么test_ts这个设备放置于/sys/devices/platform下,而不是/sys/bus/platform/devices下呢
我认为和直观性有关,在sys下有这么几个目录block  bus  class  dev  devices  firmware  kernel  module  fs power 
devices很直观的说明了设备在这个目录下
再来看组成这个目录图的核心,kobject图,我也叫他层次图


不看大号绿色箭头右边的内容的话是不是发现两个架构相同?
对的,kobject的层次决定了目录的结构
kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype
在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices
首先是bus
bus的注册在/drivers/base/bus.c里
int __init buses_ini 
 
t(void)
{
        bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
        if (!bus_kset)
                return -ENOMEM;
        return 0;
}
先看bus_uevent_ops,这是一个uevent的操作集(我也还没清楚uevent的用途,所以uevent的内容先放着)
然后到kset_create_and_add
struct kset *kset_create_and_add(const char *name,
                                 struct kset_uevent_ops *uevent_ops,
                                 struct kobject *parent_kobj)
//传递进来的参数为("bus", &bus_uevent_ops, NULL)
{
        struct kset *kset;
        int error;
        //创建一个kset容器
        kset = kset_create(name, uevent_ops, parent_kobj);
        if (!kset)
                return NULL;
        //注册创建的kset容器
        error = kset_register(kset);
        if (error) {
                kfree(kset);
                return NULL;
        }
        return kset;
}
首先需要创建一个kset容器
static struct kset *kset_create(const char *name,
                                struct kset_uevent_ops *uevent_ops,
                                struct kobject *parent_kobj)
//传递进来的参数为("bus", &bus_uevent_ops, NULL)
{
        struct kset *kset;
        //为kset分配内存
        kset = kzalloc(sizeof(*kset), GFP_KERNEL);
        if (!kset)
                return NULL;
        //设置kset中kobject的名字,这里为bus
        kobject_set_name(&kset->kobj, name);
        //设置uevent操作集,这里为bus_uevent_ops
        kset->uevent_ops = uevent_ops;
        //设置父对象,这里为NULL
        kset->kobj.parent = parent_kobj;
        //设置容器操作集
        kset->kobj.ktype = &kset_ktype;
        //设置父容器
        kset->kobj.kset = NULL;
        return kset;
}
这里的ktype,也就是kset_ktype是一个操作集,用于为sys下文件的实时反馈做服务,例如我们cat name的时候就要通过ktype提供的show函数,具体什么怎么运用,将在后面讲解
现在回到kset_create_and_add中的kset_register,将建立好的kset添加进sys里
int kset_register(struct kset *k)
{
        int err;
        if (!k)
                return -EINVAL;
        //初始化
        kset_init(k);
        //添加该容器
        err = kobject_add_internal(&k->kobj);
        if (err)
                return err;
        kobject_uevent(&k->kobj, KOBJ_ADD);
        return 0;
}
kset_init进行一些固定的初始化操作,里面没有我们需要关心的内容
kobject_add_internal为重要的一个函数,他对kset里kobj的从属关系进行解析,搭建正确的架构
static int kobject_add_internal(struct kobject *kobj)
{
        int error = 0;
        struct kobject *parent;
        //检测kobj是否为空
        if (!kobj)
                return -ENOENT;
        //检测kobj名字是否为空
        if (!kobj->name || !kobj->name[0]) {
                pr_debug("kobject: (%p): attempted to be registered with empty "
                         "name!\n", kobj);
                WARN_ON(1);
                return -EINVAL;
        }
        //提取父对象
        parent = kobject_get(kobj->parent);
        /* join kset if set, use it as parent if we do not already have one */
        //父容器存在则设置父对象
        if (kobj->kset) {//在bus的kset中为空,所以不会进入到下面的代码
                //检测是否已经设置父对象
                if (!parent)
                        //无则使用父容器为父对象
                        parent = kobject_get(&kobj->kset->kobj);
                //添加该kobj到父容器的链表中
                kobj_kset_join(kobj);
                //设置父对象
                kobj->parent = parent;
        }
        pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
                 kobject_name(kobj), kobj, __func__,
                 parent ? kobject_name(parent) : "",
                 kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
        //建立相应的目录
        error = create_dir(kobj);
        if (error) {
                kobj_kset_leave(kobj);
                kobject_put(parent);
                kobj->parent = NULL;
                if (error == -EEXIST)
                        printk(KERN_ERR "%s failed for %s with "
                               "-EEXIST, don't try to register things with "
                               "the same name in the same directory.\n",
                               __func__, kobject_name(kobj));
                else
                        printk(KERN_ERR "%s failed for %s (%d)\n",
                               __func__, kobject_name(kobj), error);
                dump_stack();
        } else
                kobj->state_in_sysfs = 1;
        return error;
}
至此bus的目录就建立起来了
模型如下


接下来是devices,在/drivers/base/core.c里
int __init devices_init(void)
{
        devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
        if (!devices_kset)
                return -ENOMEM;
        return 0;
}
过程和bus的注册一致,我就不复述了~
模型如下

然后是platform的注册
在platform的注册中,分为两个部分,一部分是注册到devices中,另一部分是注册到bus中,代码在/drivers/base/platform.c中
int __init platform_bus_init(void)
{
        int error;
        
        //注册到devices目录中
        error = device_register(&platform_bus);
        if (error)
                return error;
        //注册到bus目录中
        error =  bus_register(&platform_bus_type);
        
if (error)
                device_unregister(&platform_bus);
        return error;
}
首先是device_register,注册的参数为platform_bus,如下所示
struct device platform_bus = {
        .bus_id                = "platform",
};
很简单,只有一个参数,表明了目录名
int device_register(struct device *dev)
{
        //初始化dev结构
        device_initialize(dev);
        //添加dev至目录
        return device_add(dev);
}
void device_initialize(struct device *dev)
{
        //重要的一步,指明了父容器为devices_kset,而devices_kset的注册在前面已经介绍过了
        dev->kobj.kset = devices_kset;
        //初始化kobj的ktype为device_ktype
        kobject_init(&dev->kobj, &device_ktype);
        klist_init(&dev->klist_children, klist_children_get,
                   klist_children_put);
        INIT_LIST_HEAD(&dev->dma_pools);
        INIT_LIST_HEAD(&dev->node);
        init_MUTEX(&dev->sem);
        spin_lock_init(&dev->devres_lock);
        INIT_LIST_HEAD(&dev->devres_head);
        device_init_wakeup(dev, 0);
        set_dev_node(dev, -1);
}
int device_add(struct device *dev)
{
        struct device *parent = NULL;
        struct class_interface *class_intf;
        int error;
        dev = get_device(dev);
        if (!dev || !strlen(dev->bus_id)) {
                error = -EINVAL;
                goto Done;
        }
        pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
        parent = get_device(dev->parent);
        setup_parent(dev, parent);
        if (parent)
                set_dev_node(dev, dev_to_node(parent));
        //设置dev->kobj的名字和父对象,并建立相应的目录
        error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
        if (error)
                goto Error;
        if (platform_notify)
                platform_notify(dev);
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_ADD_DEVICE, dev);
        //建立uevent文件
        error = device_create_file(dev, &uevent_attr);
        if (error)
                goto attrError;
        if (MAJOR(dev->devt)) {
                error = device_create_file(dev, &devt_attr);
                if (error)
                        goto ueventattrError;
        }
        //建立subsystem连接文件连接到所属class,这里没有设置class对象所以不会建立
        error = device_add_class_symlinks(dev);
        if (error)
                goto SymlinkError;
        //建立dev的描述文件,这里没有设置描述文件所以不会建立
        error = device_add_attrs(dev);
        if (error)
                goto AttrsError;
        //建立链接文件至所属bus,这里没有设置所属bus所以不会建立
        error = bus_add_device(dev);
        if (error)
                goto BusError;
        //添加power文件,因为platform不属于设备,所以不会建立power文件
        error = device_pm_add(dev);
        if (error)
                goto PMError;
        kobject_uevent(&dev->kobj, KOBJ_ADD);
        //检测驱动中有无适合的设备进行匹配,但没有设置bus,所以不会进行匹配
        bus_attach_device(dev);
        if (parent)
                klist_add_tail(&dev->knode_parent, &parent->klist_children);
        if (dev->class) {
                down(&dev->class->sem);
                list_add_tail(&dev->node, &dev->class->devices);
                list_for_each_entry(class_intf, &dev->class->interfaces, node)
                        if (class_intf->add_dev)
                                class_intf->add_dev(dev, class_intf);
                up(&dev->class->sem);
        }
Done:
        put_device(dev);
        return error;
PMError:
        bus_remove_device(dev);
BusError:
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_DEL_DEVICE, dev);
        device_remove_attrs(dev);
AttrsError:
        device_remove_class_symlinks(dev);
SymlinkError:
        if (MAJOR(dev->devt))
                device_remove_file(dev, &devt_attr);
ueventattrError:
        device_remove_file(dev, &uevent_attr);
attrError:
        kobject_uevent(&dev->kobj, KOBJ_REMOVE);
        kobject_del(&dev->kobj);
Error:
        cleanup_device_parent(dev);
        if (parent)
                put_device(parent);
        goto Done;
}
在kobject_add-> kobject_add_varg-> kobject_add_internal中
//提取父对象,因为没有设置,所以为空
parent = kobject_get(kobj->parent);
//父容器存在则设置父对象,在前面的dev->kobj.kset = devices_kset中设为了devices_kset
if (kobj->kset) {
//检测是否已经设置父对象
        if (!parent)
                //无则使用父容器为父对象
                parent = kobject_get(&kobj->kset->kobj);
//添加该kobj到父容器的链表中
        kobj_kset_join(kobj);
        //设置父对象
        kobj->parent = parent;
}
现在devices下的platform目录建立好了,模型如下,其中红线描绘了目录关系

现在到bus_register了
注册的参数platform_bus_type如下所示
struct bus_type platform_bus_type = {
        .name                = "platform",
        .dev_attrs        = platform_dev_attrs,
        .match                = platform_match,
        .uevent                = platform_uevent,
        .suspend                = platform_suspend,
        .suspend_late        = platform_suspend_late,
        .resume_early        = platform_resume_early,
        .resume                = platform_resume,
};
int bus_register(struct bus_type *bus)
{
        int retval;
        //声明一个总线私有数据并分配空间
        struct bus_type_private *priv;
        priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
        //互相关联
        priv->bus = bus;
        bus->p = priv;
        BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
        //设置私有数据中kobj对象的名字
        retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
        if (retval)
                goto out;
        //设置父容器为bus_kset,操作集为bus_ktype
        priv->subsys.kobj.kset = bus_kset;
        priv->subsys.kobj.ktype = &bus_ktype;
        priv->drivers_autoprobe = 1;
        //注册bus容器
        retval = kset_register(&priv->subsys);
        if (retval)
                goto out;
        //建立uevent属性文件
        retval = bus_create_file(bus, &bus_attr_uevent);
        if (retval)
                goto bus_uevent_fail;
        //建立devices目录
        priv->devices_kset = kset_create_and_add("devices", NULL,
                                                 &priv->subsys.kobj);
        if (!priv->devices_kset) {
                retval = -ENOMEM;
                goto bus_devices_fail;
        }
        //建立drivers目录
        priv->drivers_kset = kset_create_and_add("drivers", NULL,
                                                 &priv->subsys.kobj);
        if (!priv->drivers_kset) {
                retval = -ENOMEM;
                goto bus_drivers_fail;
        }
        //初始化klist_devices和klist_drivers链表
        klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
        klist_init(&priv->klist_drivers, NULL, NULL);
        //增加probe属性文件
        retval = add_probe_files(bus);
        if (retval)
                goto bus_probe_files_fail;
        //增加总线的属性文件
        retval = bus_add_attrs(bus);
        if (retval)
                goto bus_attrs_fail;
        pr_debug("bus: '%s': registered\n", bus->name);
        return 0;
bus_attrs_fail:
        remove_probe_files(bus);
bus_probe_files_fail:
        kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
        kset_unregister(bus->p->devices_kset);
bus_devices_fail:
        bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
        kset_unregister(&bus->p->subsys);
        kfree(bus->p);
out:
        return retval;
}
在kset_register-> kobject_add_internal中
//提取父对象,因为没有设置父对象,所以为空
parent = kobject_get(kobj->parent);
//父容器存在则设置父对象,在上文中设置了父容器priv->subsys.kobj.kset = bus_kset
if (kobj->kset) {
        //检测是否已经设置父对象
        if (!parent)
                //无则使用父容器为父对象
                parent = kobject_get(&kobj->kset->kobj);
        //添加该kobj到父容器的链表中
        kobj_kset_join(kobj);
        //设置父对象
        kobj->parent = parent;
}
在retval = kset_register(&priv->subsys)完成之后platform在bus下的模型如下图


有印象的话大家还记得在platform下面有两个目录devices和drivers吧~
现在就到这两个目录的注册了
priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
注意这两条语句的头部
priv->devices_kset = kset_create_and_add
priv->drivers_kset = kset_create_and_add
可以清楚的看到bus_type_private下的devices_kset, drivers_kset分别连接到了devices,drivers的kset上
现在来看kset_create_and_add("devices", NULL,&priv->subsys.kobj);
struct kset *kset_create_and_add(const char *name,
                                 struct kset_uevent_ops *uevent_ops,
                                 struct kobject *parent_kobj)
//参数为"devices", NULL,&priv->subsys.kobj
{
        struct kset *kset;
        int error;
        //创建一个kset容器
        kset = kset_create(name, uevent_ops, parent_kobj);
        if (!kset)
                return NULL;
        //注册创建的kset容器
        error = kset_register(kset);
        if (error) {
                kfree(kset);
                return NULL;
        }
        return kset;
}
在kset_create 中比较重要的操作为
kset->kobj.ktype = &kset_ktype //设置了ktype,为kset_ktype
kset->kobj.parent = parent_kobj; //设置了父对象,为priv->subsys.kobj,也就是platform_bus_type->p->subsys.kobj
kset->kobj.kset = NULL;    //设置父容器为空
在kset_register中
//提取父对象
parent = kobject_get(kobj->parent); //在之前设置为了
//父容器存在则设置父对象,由于父容器为空,不执行以下代码
if (kobj->kset) {
        //检测是否已经设置父对象
        if (!parent)
                //无则使用父容器为父对象
                parent = kobject_get(&kobj->kset->kobj);
        //添加该kobj到父容器的链表中
        kobj_kset_join(kobj);
        //设置父对象
        kobj->parent = parent;
}
至此, devices的模型就建立好了,drivers模型的建立和devices是一致的,只是名字不同而已,我就不复述了,
阅读(167) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~