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

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

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的接口函数:

void kobject_init(struct kobject *kobj);

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

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

设置kobject的名字。

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

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

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的简单组合。旧内核称为

extern int kobject_register(struct kobject *kobj);

void kobject_del(struct kobject *kobj);

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

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对象的引用计数。

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是指向如何读写的函数的指针。

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程序的调用,该程序通过加载驱动程序,创建设备节点,挂装分区,或者其他正确的动作来响应。对热插拔事件的控制由保存在 结构体中的函数完成:

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

#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

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

测试效果:

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;这行注释掉,也就是不产生热插拔事件,效果如下:

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

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

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的测试效果:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值