现在的位置: 首页 > 综合 > 正文

kobject

2013年09月12日 ⁄ 综合 ⁄ 共 5565字 ⁄ 字号 评论关闭

内核版本: 2.6.58.8

参考资料:《linux设备驱动程序(第三版)》《linux内核设计与实现(第三版)

设备模型卡了好几天,反复折腾,思绪还是有点乱,先写出来,以后发现有错误再修改。

    2.6内核中引入设备模型,设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。Linux设备模型的目的是:为内核建立起一个对系统结构的一般性抽象描述有了设备模型,各种复杂的设备以及他们之间的层次关系就会变的很明晰。也就是说,设备模型是一种“机制”,把复杂、凌乱的东西分类分层,使之看起来简单容易操作。

现在内核使用设备模型支持多种不同的任务:
电源管理和系统关机:设备模型使操作系统能以正确顺序遍历系统硬件。
与用户空间的通讯sysfs 虚拟文件系统的实现与设备模型的紧密相关并向外界展示它所表述的结构。向用户空间提供系统信息、改变操作参数的接口正越来越多地通过sysfs也就是设备模型来完成。
热插拔设备设备模型包括了将设备分类的机制,在一个更高的功能层上描述这些设备并使设备对用户空间可见。
对象生命周期:设备模型的实现需要创建一系列机制来处理对象的生命周期、对象间的关系和对象在用户空间的表示。

以前研究过proc虚拟文件系统,它是一种基于ramram-base)的文件系统,proc中存放的是系统运行的动态信息,目的在于提供给用户一个接口,让用户能够查看系统的一些状态信息,还可以修改一些状态参数,比如printk设备模型用户层的体香就是在sysfs下,sysfs也是一种基于ram的文件系统,它是把内核的一些数据结构、数据结构的属性以及他们之间个关系报告给用户。

/sys下有这些目录:

Block:在系统中发现的每个块设备在该目录下对应一个子目录。每个子目录中又包含一些属性文件,它们描述了这个块设备的各方面属性,如:设备大小。 

Bus:在内核中注册的每条总线在该目录下对 应一个子目录如:ide pci scsi usb pcmcia 其中每个总线目录内又包含两个子目录:devicesdriversdevices 目录包含了在整个系统中发现的属于该总线类型的设备,drivers目录包含了注册到该总线的所有驱动。

Class:将设备按照功能进行的分类,如/sys/class/net 目录下包含了所有网络接口。 

Devices:包含系统所有的设备。 

Kernel:内核中的配置参数 

Module:系统中所有模块的信息 

Firmware:系统中的固件 

Fs: 描述系统中的文件系统 

Power:系统中电源选项 

前边说过,设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构,那这种连接的通过谁来完成的呢?是kobject,kobject实现了该结构并将其聚合在一起。分别研究下kobjectkset

1kobject

kobject是组成设备模型的最小单元,他是一个数据结构,kobject常被嵌入于其他类型(即:容器)中,如bus,devices,drivers都是典型的容器,用面向对象思维来说就类似于C++中的基类,这些容器通过kobject连接起来,形成了一个树,他就像一根线一样让设备和模型联系起来,表现在用户空间就是/sys下的一个个目录(后边会说到kset也是一个个目录

kobject可以做什么:

对象的应用计数

sysfs表述(上边说的/sys下的目录)

数据结构的关联(“纽带”的作用)

热插拔处理

kobject一些操作函数:

struct kobject {

const char*name; //显示的文件夹名

struct krefkref; //应用计数

struct list_headentry; //kobject之间的双向链表所属的kset形成环形链表

struct kobject*parent; //父节点,指向kset里的私有kobject

struct kset*kset; //父节点,指向上一层kset

struct kobj_type*ktype; //负责对该kobject类型进行跟踪的struct kobj_type的指针

struct sysfs_dirent*sd; 

unsigned int state_initialized:1; //kobject是否初始化

unsigned int state_in_sysfs:1; //是否已经加入sysfs

unsigned int state_add_uevent_sent:1;

unsigned int state_remove_uevent_sent:1;

};

cdev结构里就含有一个kobject,可以把他连接到内核的整个驱动模型中。

struct cdev {

struct kobject kobj;

struct module *owner;

const struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

struct cdev *device = container_of(kg, struct cdev, kobj) 

//得到的是指向包含kobject的结构体指针,这里边就是cdev

kobject的几个操作函数:

void *memset(void *s, int c, size_t count)

//将整个kobject设置为0

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

 struct kobject *parent, const char *fmt, ...)

//kobject的初始化,并将其注册到linux系统,这里初始化之后应用计数为1

int kobject_set_name(struct kobject *kobj, const char *name, ...)

//设置名字

void kobject_del(struct kobject * kobj) 

//Linux系统中删除kobject对象

struct kobject *kobject_get(struct kobject *kobj) 

//应用计数加1

void kobject_put(struct kobject *kobj) 

//应用计数减1

分析几个主要函数

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

if (!kobj) { //检查kobject变量是否为空

err_str = "invalid kobject pointer!";

goto error;

}

if (!ktype) { //检查kobj_type变量是否为空

err_str = "must have a ktype to be initialized properly!\n";

goto error;

}

if (kobj->state_initialized) { //是不是已经初始化过了

/* do not error out as sometimes we can recover */

printk(KERN_ERR "kobject (%p): tried to init an initialized "

       "object, something is seriously wrong.\n", kobj);

dump_stack(); //调试用的

}

kobject_init_internal(kobj); //进一步初始化

kobj->ktype = ktype;

return;

error:

printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

dump_stack();

}

static void kobject_init_internal(struct kobject *kobj)

{

if (!kobj)

return;

kref_init(&kobj->kref); //调用kref_set(kref, 1),应用计数设置为1

INIT_LIST_HEAD(&kobj->entry); //初始化链表

kobj->state_in_sysfs = 0; //kobject 还没有注册到sysfs,所以设置为0

kobj->state_add_uevent_sent = 0; //事件相关的

kobj->state_remove_uevent_sent = 0;

kobj->state_initialized = 1; //设置为初始化之后

}

可以看到写了这么多代码就做了三件事:

1kobject应用计数设为1

2、初始化kobject链表

3、设置初始化标志(state_initialized = 1)、注册和时间标志

kobject_add就是把这个kobject注册到linux系统,后边专门分析

这里要注意的几点:

1kobject初始化之后应用计数就变为1,所以创建当创建kobject后,如果不再需要初始的应用,就要调用相应的kobject_put函数将应用计数减为0,要不会出现错误。

2kobject中的应用计数不能够防止竞态的产生。

3、应用计数不为创建kobject的代码所直接控制,因此当kobject的最后一个应用计数不在存在时,必须异步通知,也就是说每个kobject都必须有一个release方法,这个方法并没有在kobject结构体中,而是定义到kobj_type中。

2kobj_type

kobj_type中的release成员中保存的是这种kobject类型的release函数指针,(我觉得多个kobject可以共用一个kobj_type,但是每个kobject都的有kobj_type,不知道对否)kobj_type的是对象的类。

struct kobj_type {

void (*release)(struct kobject *kobj);

/*移除kobject,kobject_put最后会检测应用计数,

 *当为0须释放该kobject,后边会说到

 */

struct sysfs_ops *sysfs_ops; //属性文件操作函数,这里只有读和写

struct attribute **default_attrs; //属性数组,表现出来是一个个文件

};

struct sysfs_ops {

ssize_t (*show)(struct kobject *, struct attribute *,char *);

   //当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态

ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);

   //当用户写属性文件时,该函数被调用,用于存储用户传入的属性值

};

struct attribute {

const char *name; //文件名

struct module *owner; //所属模块

mode_t mode; //文件读取权限

};

struct kobject *kobject_get(struct kobject *kobj)

{

if (kobj)

kref_get(&kobj->kref);

return kobj;

}

void kref_get(struct kref *kref)

{

WARN_ON(!atomic_read(&kref->refcount));

atomic_inc(&kref->refcount); //对原子变量kref->refcount原子的增加1

smp_mb__after_atomic_inc();

//后边这两个都属于原子操作的问题

//http://www.kgdb.info/linuxdev/linux_memory_barriers/有详细描述

}

void kobject_put(struct kobject *kobj)

{

if (kobj)

kref_put(&kobj->kref, kobject_release);

}

int kref_put(struct kref *kref, void (*release)(struct kref *kref))

{

WARN_ON(release == NULL); //空警告

WARN_ON(release == (void (*)(struct kref *))kfree); 

//表明释放函数不能是kfreeLDD3有说明

if (atomic_dec_and_test(&kref->refcount)) {

release(kref); //这里会用到release函数,释放该kobject

return 1;

}

return 0;

}

这里的release函数就是kobj_type里边定义的那个release,每一个kobject都有唯一的release但是每个kobj_type不是针对于一个kobject,而是一类,也就是一个release函数可以对一类kobject释放,至于那些应该是一类?我觉得应该是所有 kobject中的parent指针指向同一个kset中的私有kobject 的kobject就应该是一类。

 

抱歉!评论已关闭.