linux内核驱动模型

linux内核驱动模型,以2.6.32内核为例。(一边写一边看的,有点乱。)

1、以内核对象为基础。用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键。
具有相同类型的内核对象构成内核对象集,用kset表示,内核对象集也包含自己的内核对象,从而组成层次化的结构。
2、用sysfs文件系统导出到用户空间。内核中的所有内核对象组织成树状,以对象属性为叶子。
通过sysfs文件系统,将用户空间对文件的读写操作转化为对内核对象属性的显示和保存方法。从而导出内核对象信息,并提供配置接口。
3、将linux子系统表达为总线类型、驱动、设备、类、接口的关系,分别用bus_type、device_driver、device、class、class_interface结构表示。
每个子系统有自己的总线类型,它有一条驱动链表和一条设备链表,用来链接已加载的驱动和已发现的设备,驱动加载和设备发现的顺序可以是任意的。
每个设备可以被绑定到最多一个驱动,被绑定了驱动的设备可以正常工作。
除此之外,每个设备可以属于某个唯一的类,在类上包含多个接口,接口的方法被用于设备,不管是先添加接口,还是先发现设备。

内核引用计数kref
在程序动态运行的情况下,常常需要将一个内核分配好的对象多次传递和使用。为了在该对象不再被使用时回收其内存资源,需要对该对象的引用进行计数。
kref有两个好处:防止内存泄露,在对象不被使用时释放内存;防止访问已释放的内存,确保不会访问已释放的对象。
kref的操作
kref_init():初始化对象为1,而不是0,因为生成该对象的代码也需要一个最初的引用,以防止其他部分在调用kref_put()时释放对象。
kref_get():递增对象的引用计数。
kref_put():递减对象的引用计数,如果计数为0,则调用传入的release方法。

kset具有以下功能:
作为包含一组对象的容器,kset可以被内核用来跟踪“所有块设备”或者“所有PCI设备驱动”。
作为一个目录级的“粘合剂”,将设备模型中的内核对象(以及sysfs)黏在一起。每个kset都内嵌一个kobject,可以作为其他kobject的父亲,通过这种层次构造设备模型层次。
kset可以支持kobject的“热插拔”,影响热插拔事件被报告给用户空间的方式。

举个形象的例子:
kset好比一个文件夹,kobject是文件夹下面的内容,kobject的类型可以不同,好比文件夹下有图片、视频、文档、程序等。
不同的kset好比不同的文件夹,但它们下面可以有相同类型的kobject。比如文件夹A和B下面都有图片和视频。
kset自身也有一个kobject,好比文件夹本质上也是一个文件一样,如同视频、图片之类的。
通过kset的list能找到下面的kobject,即找到这个文件夹,就能找到该文件夹下面的内容。
kobject的kset指向该kobject所属的kset,即文件夹下面的内容都属于这个文件夹之内。
kset通过kobject的entery将kobject组织起来,类似于文件夹下面的文件的路径都是相同的。
kobject的parent可以指向同kset下的另一个kobject,或者NULL,或者kset的kobject。

1、创建或初始化内核对象:kobject_create()和kobject_init()。两者的区别是前者会为kobject分配存储空间,然后调用kobject_init(),而后者是用于初始化已经分配了内存的kobject()。
2、将内核对象添加到sysfs:kobject_add()。该函数只会为默认属性自动创建sysfs文件。如果有其他属性,则需要另外创建sysfs文件。
kobject的parent决定该kobject在sysfs树中的位置。
1)若kobject的parent和kset都是空,则该kobject在sysfs树的根目录下。
2)若kobject的parent是空,kset已设置,则该kobject在sysfs树的位置在kset的kobject目录下。
3)若kobject的parent和kset都已设置,则该kobject在sysfs的位置在parent下。
3、创建、初始化、添加内核对象集:
kset_init()、kset_create()两者的区别和kobject_create()、kobject_init()的一样。
kset_add():添加内核对象集到sysfs
kset_register():kset_init()+kset_add()
kset_create_and_add():等价于kset_create+()kset_add()
4、发送内核对象变化事件到用户态空间
kobject_uevent()->kobject_uevent_env()

sysfs
从两个角度理解:
1、从内部看,sysfs是一种表示内核对象、对象属性,以及对象关系的一种机制。sysfs核心将内核输出的对象、对象属性以及对象关系组织成树状结构,称为sysfs内部树。
2、从外部看,sysfs类似于proc文件系统,用于将系统中的设备组织成层次结构(sysfs外部树),向用户空间导出设备和驱动信息,并且为设备和驱动提供配置接口。
sysfs核心负责建立内部树和外部树的对应关系,也称为sysfs映射。
1)内核对象被映射为用户空间的目录
2)对象属性被映射为用户空间的常规文件
3)对象关系被映射为用户空间的符号链接
对sysfs的读写转换成对属性的show和store操作。

总线类型
结构体:bus_type、bus_type_private
操作:bus_register()、bus_unregister()、bus_for_each_dev()、bus_for_each_drv()

设备
结构体:device、device_private
操作:device_register()、device_unregister()

驱动
结构体:device_driver、driver_private
操作:driver_register()、driver_unregister()


结构体:class、class_private
操作:class_register()、class_unregister()

接口
结构体:class_interface
操作:class_interface_register()、class_interface_unregister()

普通双向链表
struct foo {
struct foo * prev;
struct foo * next;
int val;
};
prev/next是类型相关的,都是struct foo *型。如果要在另外一个struct foo1中使用链表,必须重新定义foo1私有的链表。
struct foo1 {
struct foo1 * prev;
struct foo1 * next;
int val;
char c;
};
foo和foo1的链表操作,本质上虽然相同,但由于两者类型不同,所以不同通用。
比如插入,foo的插入,操作的是struct foo *型;而foo1的插入,操作的是stcut foo1 *型。

内核链表
struct list_head {
struct list_head * prev;
struct list_head * next;
};
struct foo {
struct list_head list;
int val;
};
prev/next是类型无关的,都是struct list_head*型,和所在结构体无关。如果要在另一个结构体foo1中使用链表,只需在foo1中包含该成员即可。
struct foo1 {
struct list_head list;
int val;
char c;
};
链表的操作可以通用,都是操作struct list_head *型。

bus_register()

时间: 2024-11-03 21:32:37

linux内核驱动模型的相关文章

Linux RTC驱动模型分析

RTC简介 RTC(real-time clock)简称实时时钟,主要作用是用来记时,产生闹钟等.RTC因为有备份电池,所以即使计算机关机掉电,也不会影响RTC记时.而RTC和系统时间(主要靠软件模拟)的区别在于,RTC会在掉电后数据不丢失,在下次启动依旧可以重新设置当前时间给计算机.而系统时间主要靠软件模拟产生,在掉电之后会丢失,需要在下次计算机重新启动之后重新模拟产生.RTC时间在每次系统启动的时候会使用,在以后需要的时候会将设置的时间写入到RTC中,别的时候获取时间都通过软件可以获得. R

Linux 设备驱动模型

Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的数据或回送应用程序请求的数据 4,检测或处理设备出现的错误 设备驱动模型提供了硬件的抽象包括: 1,电源管理 其实,电源管理就是一些设备不工作的时候,让它歇一会,休眠一会(最低消耗),达到省电的目的 它的一个重要的功能是: 省电模式下,使系统中的设备以一定的先后顺序挂起 在全速工作模式下,使系统的设

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

linux 内核驱动--Platform Device和Platform_driver注册过程

linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Platform_driver . Linux 中大部分的设备驱动,都可以使用这套机制 , 设备用 Platform_device 表示,驱动用 Platform_driver 进行注册. Linux platform driver 机制和传统的 device driver 机制 ( 通过 driver_

Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2G 8. 选择网络地址转换 9.设置IO控制器 10. 选择默认磁盘类型 11.创建一个新的虚拟磁盘 12.设置磁盘大小 13.选择路径保存虚拟磁盘 14. 完成虚拟机创建 15.设置虚拟机 16.选择opensuse镜像 17.开启虚拟机 18.虚拟机启动 19.安装opensuse 20.安装程

Linux内核驱动注册方式泛谈

Linux驱动注册有多种方式,通常是以内核提供的表征数据结构封装后按照内核子系统提供的接口函数进行注册,还有一些是比较复杂的以链表方式进行维护.以下对几种驱动注册方式进行介绍: 一.子系统有专门的驱动注册函数: 例如RTC子系统,提供rtc_device_register注册接口函数. 例如: rtc_device_register(client->name,&client->dev, &rx8025_rtc_ops, THIS_MODULE); static struct r

Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" /> 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. watermark/2/text/a

你为什么看不懂Linux内核驱动源码?

学习嵌入式Linux驱动开发,最核心的技能就是能够编写Linux内核驱动.深入理解Linux内核.而做到这一步的基础,就是你要看得懂Linux内核源码,了解其基本的框架和具体实现,了解其内核API的使用方法,然后才能根据自己的需求写出高质量的内核驱动程序. 说易行难,很多新人.甚至工作1-2年的开发者刚接触Linux内核时,别说写了,看内核代码可能都是一脸懵逼:明明是C语言,但是就是看不懂是什么意思,除了根据函数名.函数参数.函数的返回值以及注释,了解整个函数的基本功能外,一旦分析其细节,你会发

linux内核驱动学习指南

目录: 1.参考链接 1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动 原文地址:https://www.cnblogs.com/agui125/p/10071452.html