您的位置:首页 > 运维架构 > Linux

Linux设备模型(上)之底层模型

2012-03-15 22:29 381 查看
努力成为linux kernel hacker的人李万鹏原创作品,转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/05/6396618.aspx

如果哪里有理解不对的请指教,文章引用的内核源码版本为2.6.29.1的。

建立设备模型主要为了管理方便。最初引入设备模型是为了电源管理。建立一个全局的设备树(device tree),当系统进入休眠时,系统可以通过这棵树找到所有的设备,随时让他们挂起(suspend)或者唤醒(resume)。

2.6版内核提供的功能:

电源管理和系统关机

完成这些工作需要对一些系统结构的理解。比如一个USB宿主适配器,在处理完所有与其连接的设备前是不能被关闭的。设备模型使得操作系统能够以正确的顺序遍历硬件。

与用户空间通信

sysfs虚拟文件系统的实现与设备模型密切相关,并且向外界展示了它所表示的结构。向用户空间所提供的系统信息,以及改变操作参数的接口,将越来越多的通过sysfs实现,也就是说通过设备模型实现。

热插拔设备

内核中的热插拔机制可以处理热插拔设备,特别是能够与用户空间进行关于热插拔设备的通信,而这种机制也是通过热插拔管理的。

设备类型

把设备分门别类有助于设备的管理与使用。比如要找USB鼠标,只要去classes/input/里去找就可以了,而不必关心这个鼠标是接到哪个USB主机控制器的哪个Hub的第几个端口上。

对象生命周期

得有一个好的机制来实现设备生命周期的管理。比如把USB鼠标拔了之后,全局设备树和sysfs里面得相应去掉。

设备底层模型:

Linux设备模型的底层是数据结构kobject,内核用kobject结构将各个对象连接起来组成一个分层的结构体系,从而与模块化的子系统相匹配。一个kset是嵌入相同类型结构的kobject集合。kset和他的kobject的关系与下图类似,请记住:

ü 在图中所有被包含的kobject,实际上是被嵌入到其他类型中的,甚至可能是其他的kset。

ü 一个kobject的父节点不一定是包含它的kset。



Kobject是组成设备模型的基本结构,最初他只是被理解为一个简单的引用计数,但是随着时间的推移,他的任务越来越多,因此也有了许多成员,他的结构体如下:

[c-sharp] view plaincopy

struct kobject {
const char *name;
struct list_head entry; //挂接到所在kset中去的单元
struct kobject *parent; //指向父对象的指针
struct kset *kset; //所属kset的指针
struct kobj_type *ktype; //指向其对象类型描述符的指针
struct sysfs_dirent *sd; //sysfs文件系统中与该对象对应的目录实体的指针
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};

name指向设备的名字,entry,parent,kset就是用来形成树状结构的指针。Kobj_type *type用来表示该kobject的类型,struct sysfs_dirent类型的指针指向了该kobject在sysfs中的目录实体,sysfs中每一个dentry都会对应一个sysfs_dirent结构。每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。/sys是专为Linux设备模型建立的,kobject是帮助建立/sys文件系统的。每当我们新增一个kobject结构时,同时会在/sys下创建一个目录。这里隐藏了如下程序调用流程:kobject_add()->kobject_add_varg()->kobject_add_internal()->create_dir()->sysfs_new_dirent()。在sysfs_new_dirent()函数中通过slab分配了一个dirent(至于什么是dirent会在<Linux设备模型(下)之sysfs文件系统>中讲解),并返回给一个指向这个dirent的指针sd给create_dir(),在create_dir()函数中有这么一句:sd->s_dir.kobj
= kobj;也就是让dirent的一个成员的域指向了他所对应的kobject,kobject中struct sysfs_dirent *sd;又指向了dirent,所以kobject与sysfs死死的拥抱在一起,为了幸福的明天。在kobject_del()函数中会调用sysfs_remove_dir(),sysfs_remove_dir()中有这么一句:kobj->sd = NULL;表示kobject与sysfs_dirent的婚姻破裂了。

kobject的接口函数:

[c-sharp] view plaincopy

void kobject_init(struct kobject *kobj);

kobject初始化函数,设置kobject引用计数为1。

[c-sharp] view plaincopy

int kobject_set_name(struct kobject *kobj, const char *format, …);

设置kobject的名字。

[c-sharp] view plaincopy

struct kobject *kobject_get(struct kobject *kobj);
struct kobject *kobject_put(struct kobject *kobj);

减少和增加kobject的引用计数。

[c-sharp] view plaincopy

extern int __must_check kobject_init_and_add(struct kobject *kobj,
struct kobj_type *ktype,
struct kobject *parent,
const char *fmt, ...);

kobject注册函数,该函数只是kobjec_init和kobject_add_varg的简单组合。旧内核称为

[c-sharp] view plaincopy

extern int kobject_register(struct kobject *kobj);

[c-sharp] view plaincopy

void kobject_del(struct kobject *kobj);

从Linux设备层次中(hierarchy)中删除kobj对象。

[c-sharp] view plaincopy

struct kset {
struct list_head list; //用于连接该kset中所有kobject的链表头
spinlock_t list_lock; //用于互斥访问
struct kobject kobj; //嵌入的kobject
struct kset_uevent_ops *uevent_ops;
};

包含在kset中的所有kobject被组织成一个双向循环链表,list真是该链表的链表头。kset数据结构还内嵌了一个kobject对象(由kobj表示),所有属于这个kset的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的kobject对象的引用计数。

[c-sharp] view plaincopy

struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};

释放kobject使用release函数,release函数并没有包含在kobject自身内,他包含在与kobject相关联的kobj_type中。sysfs_ops是指向如何读写的函数的指针。

[c-sharp] view plaincopy

struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};

show相当于read,store相当于write。

struct attribute **default_attrs;是属性数组。在sysfs中,kobject对应目录,kobject的属性对应这个目录下的文件。调用show和store函数来读写文件,就可以得到属性中的内容。

一个热插拔事件是从内核空间发送到用户空间的通知。它表明系统配置出现了变化。无论kobject被创建还是被删除,都会产生这种事件。比如,当数码相机通过USB线缆插入到系统时。热插拔事件会导致对/sbin/hotplug程序的调用,该程序通过加载驱动程序,创建设备节点,挂装分区,或者其他正确的动作来响应。对热插拔事件的控制由保存在 结构体中的函数完成:

[c-sharp] view plaincopy

struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};

我们可以在kset结构的hotplug_ops成员中发现指向这个结构的指针。如果在kset中不包含一个指定的kobject,内核将在分层结构中进行搜索(通过parent指针),直到找到一个包含有kset的kobject为止,然后使用这个kset的热插拔操作。下面是一个测试的程序:

kobject.c

[c-sharp] view plaincopy

#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>

MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");

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);
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()
{
printk("kboject test init./n");
kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");
return 0;
}

static int kobj_test_exit()
{
printk("kobject test exit./n");
kobject_del(&kobj);
return 0;
}

module_init(kobj_test_init);
module_exit(kobj_test_exit);

kset.c

[c-sharp] view plaincopy

#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>

MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");

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,struct kobj_uevent_env *env)
{
int i = 0;
printk("uevent: kobj %s./n",kobj->name);

while( i < env->envp_idx){
printk("%s./n",env->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);

测试效果:

[c-sharp] view plaincopy

root@hacker:/home/hacker/kobject# insmod kset.ko
root@hacker:/home/hacker/kobject# dmesg
[ 866.344079] kset test init.
[ 866.344090] Filter: kobj kset_c.
[ 866.344092] Name: kobj kset_c.
[ 866.344097] uevent: kobj kset_c.
[ 866.344099] ACTION=add.
[ 866.344101] DEVPATH=/kset_p/kset_c.
[ 866.344103] SUBSYSTEM=kset_name.

root@hacker:/home/hacker/kobject# rmmod kset
root@hacker:/home/hacker/kobject# dmesg
[ 892.202071] kset test exit.
[ 892.202075] Filter: kobj kset_c.
[ 892.202077] Name: kobj kset_c.
[ 892.202083] uevent: kobj kset_c.
[ 892.202085] ACTION=remove.
[ 892.202087] DEVPATH=/kset_p/kset_c.
[ 892.202089] SUBSYSTEM=kset_name.

如果将kset_c.kobj.kset = &kset_p;这行注释掉,也就是不产生热插拔事件,效果如下:

[c-sharp] view plaincopy

root@hacker:/home/hacker/kobject# insmod kset.ko
root@hacker:/home/hacker/kobject# dmesg
[ 94.146759] kset test init.

无论什么时候,当内核要为指定的kobject产生事件时,都要调用filter函数。如果filter返回0,将不产生事件,这里将返回值改为0,看效果:

[c-sharp] view plaincopy

root@hacker:/home/hacker/kobject# insmod kset.ko
root@hacker:/home/hacker/kobject# dmesg
[ 944.457502] kset test init.
[ 944.457535] Filter: kobj kset_c.

root@hacker:/home/hacker/kobject# rmmod kset
root@hacker:/home/hacker/kobject# dmesg
[ 962.514146] kset test exit.

下边是kobject的测试效果:

[c-sharp] view plaincopy

root@hacker:/home/hacker/kobject# insmod kobject.ko
root@hacker:/home/hacker/kobject# dmesg
[ 1022.694855] kboject test init.

root@hacker:/home/hacker/kobject# rmmod kobject
root@hacker:/home/hacker/kobject# dmesg
[ 1056.650200] kobject test exit.

root@hacker:/sys/kobject_test# ls
kobj_config
root@hacker:/sys/kobject_test# cat kobj_config
root@hacker:/sys/kobject_test# dmesg
[ 1280.545220] have show.
[ 1280.545226] attrname:kobj_config.
root@hacker:/sys/kobject_test# echo "aha" > kobj_config
root@hacker:/sys/kobject_test# dmesg
[ 1280.545220] have show.
[ 1280.545226] attrname:kobj_config.
[ 1295.622228] havestore
[ 1295.622235] write: aha
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: