Linux设备驱动工程师之路——设备模型(上)底层模型
K-Style
转载请注明来自于衡阳师范学院08电2 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498
一、重要知识点
1.Sysfs文件系统
Sysfs文件系统是一种类似于proc文件系统的特殊文件系统,它存在于内存当中,当系统启动时由内核挂载于内存当中。用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的数据结构信息。
2.Linux设备底层模型
1)为什么要使用设备模型
随着系统的拓扑结构越来越复杂,以及要支持诸如电源管理等新特性的要求,于是在2.6的内核中出现了设备模型。设备模型其实就是一套数据结构建立起来的模型。内核使用该模型支持了多种不同的任务,包括:
a.电源管理和系统关机
设备模型使操作系统能够以正确的顺序遍历系统硬件。
b.与用户空间通信
Sysfs文件系统向用户空间提供系统信息以及改变操作参数的结构。
c.热插拔事件
d.设备类型
系统中许多部分对设备如何连接不感兴趣,但是他们需要知道哪些类型设备时可用的。设备模型提供了将设备分类的机制。
e.对象的生命周期
上述的许多功能,包括热插拔支持和sysfs,使得内核中管理对象的工作更为复杂。设备模型需要创造一套机制管理对象的生命周期。
2)Kobject
如果说设备模型是一套房子的话,Kobject就是构造房子的砖块。每个注册的Kobject的都对应与Sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。类似于C++的基类,它潜入于更大的对象中——所谓的容器,用来描述设备模型的组件。如bus,device,drivers都是典型的容器。这些容器就是通过kobject连接起来,形成一个树状结构。这个树状结构就与/sys文件系统对应。不过kobject只能建立单层结构,也就是只能建立一级目录,要建立多级目录,还要使用后面要介绍的Kset。
Kobject结构定义为:
struct kobject {
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文件系统中与该对象对应的文件节点路径指针
};
相关操作函数:
void kobjet_init(struct kobject*kobj)
初始化Kobject
int kobject_add(struct kobject*kobj)
将Kobject对象注册到linux系统,如果失败则返回一个错误码.
int kobject_init_and_add(structkobject *kobj, kobj_type *ktype, struct kobject *parent, const *fmt…)
初始化并注册kobject,kobject传入要初始化的Kobject对象,ktype将在后面介绍到,parent指向上级的kobject对象,如果指定位NULL,将在/sys的顶层创建一个目录。*fmt为kobject对象的名字。
kobject的ktype对象是一个指向kobject_type结构的指针,该结构记录了kobject对象的一些属性。每个kobject都需要对应一个相应的kobject结构。
struct kobj_type{
void(*release)(struct kobject *kobj);
structsysfs_ops *sysfs_ops;
structattribute **default_attrs;
};
release方法用于释放kobject占用的资源,当kobject引用计数为0时被调用。
kobje_type的attribute成员:
struct attribute{
char*name;//属性文件名
structmodule *owner;
mode_tmode;
}
struct attribute(属性):对应于kobject的目录下一个文件,name就是文件名。
kobje_type的struct sysfs_ops成员:
struct sysfs_ops
{
ssize_t (*show)(structkobejct *, struct attribute *, char *name);
ssize_t (*store)(structkobejct *, struct attribute *, char *name);
}
show:当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态;
store:当用户写属性文件时,该函数被调用,用于存储用户存入的属性值。
Kobject测试模块:
#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);
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()
{
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);
测试结果:
在/sys目录下创建了kobject_test目录
在kobject_test目录下有kobj_config文件
读kobject_config文件则调用了show函数。并在用户空间显示了show返回的kobject对象名字。
写kobject_config文件调用了store函数。
3)Kset
kset的主要功能是包容;我们可以认为他他是kobject的顶层容器。实际上,在每个kset对象的内部,包含了自己的kobject,并且可以用多种处理kobject的方法处理kset。如果说kobject是基类的话,那么kset就是派送类。kobject通过kset组织成层次化的结构,kset是相同类型的组合。通俗的讲,kobject建立一级的子目录,kset可以为kobject建立多级的层次性的父目录。
struct kset {
struct subsystem * subsys; 所在的subsystem的指针
struct kobj type * ktype; 指向该kset对象类型描述符的指针
struct list head list; 用于连接该kset中所有kobject的链表头
struct kobject kobj; 嵌入的kobject
struct kset_uevent_ops * uevent_ops; 指向热插拔操作表的指针
};
包含在kset中的所有kobject被组织成一个双向循环链表,list域正是该链表的头。Ktype域指向一个kobj type结构,被该kset中的所有kobject共享,表示这些对象的类型。Kset数据结构还内嵌了一个kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的kobject对象的引用计数。
kset与kobject的关系图
Kset操作:
int kset_register(struct kset*kset)
注册kset
void kset_unregister(struct kset*kset)
注销kset
热插拔事件:在linux系统中,当系统配置发生变化时,如添加kset到系统或移动kobject,一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。
对热插拔事件的实际控制是由struct kset_uevent_ops结构中的函数完成的。
struct kset_uevnt_ops{
int (*filter)(struct kset *kset,struct kobject *kobj);
const char *(*name)(struct kset *kset, structkobject *kobj );
int (*uevent)(struct kset *kset,struct kobject *kobj,struct kobj_uevent *env);
}
filter决定是否产生事件,如果返回0,将不产生事件。
name向用户空间传递一个合适的字符串
uevent通过环境变量传递任何热插拔脚本需要的信息,他会在(udev或mdev)调用之前,提供添加环境变量的机会。
kset测试模块:
#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);
测试结果:
可以看出当kset加载时,在/sys下创建了一个kset_p,在kset_p下面创建了kset_c,当kset模块被加载和卸载时都产生了热插拔事件。
参考书籍:
《linux那些事儿之我是sysfs》
《linux设备驱动程序(第三版)》
《国嵌课件》
PS:
请关注下一篇《Linux设备驱动工程师之路——设备模型(下)上层模型》
写这玩意儿把我累坏了,呵呵。
分享到:
相关推荐
将《linux设备驱动第三版》中文版、英文版(均是高清)打包,并包含第三版的源代码还有笔记,有此linux设备驱动第三版便全了。希望对学习设备驱动的你有所帮助
深入linux设备驱动程序内核机制 pdf深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序...
Linux下的硬件驱动——USB设备(下)
[野火]《嵌入式Linux驱动开发实战指南——基于i.MX6ULL系列》
Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介) - Linux设备驱动程序
Linux内核驱动书籍之《Linux设备模型(上)之底层模型.pdf 》,适合深入Linux内核。
精通LINUX设备驱动程序开发
Linux设备驱动程序学习(14)-Linux设备模型(各环节的整合) - Linux设备驱动程序
·Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介) ·Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类) ·Linux设备驱动程序学习(14)-Linux设备模型(各环节的整合) ·Linux...
LINUX设备驱动程序 嵌入式开发资料
很详细的j分析了Linux下的设备模型
《Linux设备驱动开发详解(第《Linux设备驱动开发详解(第2版)》内容全面,实例丰富,操作性强,语言通俗易懂,适合广大Linux开发人员、嵌入式工程师参考使用。 图书目录 第1篇 Linux设备驱动入门 第1章 Linux...
这是我跟着网络牛人和经典书籍《linux设备驱动程序》学习linux底层驱动时摘抄和自写的学习笔记,希望对大家有用,源代码我之后上传。
linux设备驱动模型 1、kobject原理与实例分析 2、kset原理与实例分析 3、bus(总线)原理与实例分析 4、device(设备)原理与实例分析 5、driver(驱动)原理与实例分析
Linux内核驱动开发详解,基于kernel4.0,对驱动工程师很有帮助
对_Linux_设备驱动模型的一些理解----张俊岭
《Linux设备驱动开发详解-基于最新的Linux4.0内核》相关源码
《Linux设备驱动开发详解》是一本介绍Linux设备驱动开发理论、框架与实例的书,《Linux设备驱动开发详解(第2版)》基于LDD6410开发板,以Linux2.6 版本内核为蓝本,详细介绍自旋锁、信号量、完成量、中断顶/底半部、...
第7章 Linux设备驱动程序开发 7.1 设备驱动概述 7.1.1 设备驱动和文件系统的关系 7.1.2 设备类型分类 7.1.3 内核空间和用户空间.. 7.2 设备驱动基础 7.2.1 设备驱动中关键数据结构 7.2.2 字符设备驱动开发 ...
Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类) - Linux设备驱动程序