mdev的基本工作原理【转】
转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077435.html
分析过mdev(udev的BusyBox简化版)源码的都知道mdev的基本原理:
a、执行mdev -s命令时,mdev扫描/sys/block(块设备保存在/sys/block目录下,内核2.6.25版本以后,块设备也保存在/sys /class/block目录下。mdev扫描/sys/block是为了实现向后兼容)和/sys/class两个目录下的dev属性文件,从该dev 属性文件中获取到设备编号(dev属性文件以"major:minor\n"形式保存设备编号),并以包含该dev属性文件的目录名称作为设备名 device_name(即包含dev属性文件的目录称为device_name,而/sys/class和device_name之间的那部分目录称为 subsystem。也就是每个dev属性文件所在的路径都可表示为/sys/class/subsystem/device_name/dev),在 /dev目录下创建相应的设备文件。例如,cat /sys/class/tty/tty0/dev会得到4:0,subsystem为tty,device_name为tty0。
b、当mdev因uevnet事件(以前叫hotplug事件)被调用时,mdev通过由uevent事件传递给它的环境变量获取到:引起该uevent 事件的设备action及该设备所在的路径device path。然后判断引起该uevent事件的action是什么。若该action是add,即有新设备加入到系统中,不管该设备是虚拟设备还是实际物理 设备,mdev都会通过device path路径下的dev属性文件获取到设备编号,然后以device path路径最后一个目录(即包含该dev属性文件的目录)作为设备名,在/dev目录下创建相应的设备文件。若该action是remote,即设备已 从系统中移除,则删除/dev目录下以device path路径最后一个目录名称作为文件名的设备文件。如果该action既不是add也不是remove,mdev则什么都不做。
由上面可知,如果我们想在设备加入到系统中或从系统中移除时,由mdev自动地创建和删除设备文件,那么就必须做到以下三点:1、在/sys/class 的某一subsystem目录下,2、创建一个以设备名device_name作为名称的目录,3、并且在该device_name目录下还必须包含一个 dev属性文件,该dev属性文件以"major:minor\n"形式输出设备编号。
思路有了,剩下的就是如何实现了。下面通过介绍linux设备驱动模型里两个重要的数据结构:class和class_device,顺带讲述如何架构mdev驱动程序,并给出2个例子:HelloWorld和udev_demo。
注意:下面讲到的结构体、函数以及例子都是针对linux-2.6.24.7版本内核,并不适合于所有版本的linux内核。各版本内核的结构体和函数原型可能有变。比如我在Ubuntu9.04下make一下第二个驱动例子,出现了好几个错误,后来发现Ubuntu9.04的内核linux-2.6.28里面关于linux设备驱动模型的数据结构有了很大改变,没有了class_device。所以当你跑下面两个例子时,如不是linux-2.6.24.7版本内核,那就要注意一下例子里使用数据结构与你内核里的数据结构是否一致。
1、class
一个类是一个设备的高层视图,它抽象掉了底层的实现细节。例如,在驱动层面时,你可能会见到SCSI磁盘或者ATA磁盘;但在类层面时,它们都是磁盘。类允许用户空间基于它们做什么来使用设备,而不是它们如何被连接或者它们如何工作。
class表示一类设备,所有class都属于class_subsys(class子系统),即出现在/sys/class目录下,除了块设备(可能出现在/sys/block/或/sys/class/block,上面讲过了)。
其实,class在/sys/class下生成的目录也就是上面提到subsystem。这样第1点就有了。
/* class结构体 */ structclass{
constchar * name; /* class的名称 */
struct module * owner; /* 拥有该class的模块 */
struct kset subsys; /* 该class对应的子系统 */
struct list_head children; /* 该class的class_device列表 */
struct list_head devices;
struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks both the children and interfaces lists */
struct class_attribute * class_attrs;/* 该class的默认属性,以NULL结尾 */
struct class_device_attribute * class_dev_attrs;/* 添加到class的class_device所拥有的默认属性 */
struct device_attribute * dev_attrs;
/* 该函数提供在产生热插拔class_device事件时,添加环境变量的能力 */
int (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);
/* 该函数提供在产生热插拔device(物理设备)事件时,添加环境变量的能力 */
int (*dev_uevent)(struct device *dev,struct kobj_uevent_env *env);
/* 添加到class的class_device移除时,调用该函数进行必要的清理工作 */
void(*release)(struct class_device *dev);
/* class被移除时,调用该函数进行必要的清理工作 */
void(*class_release)(structclass*class);
void(*dev_release)(struct device *dev);
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
};
/* class注册函数 */
int __must_check class_register(structclass*);
void class_unregister(structclass*); 建立一个class有两种方法
a、根据需要,填充一个struct class,然后再调用class_register注册该class就ok了(此法比较灵活,可以自己定制很多东西)
b、就是通过下面的class_create来创建一个class,该函数会返回一个指向刚建立的class的指针(创建class的最简单方法)
/* class_create用于创建一个名为name的class,其owner参数一般为THIS_MODULE。class_create内部调用了class_register */
structclass*class_create(struct module *owner,constchar*name);
/* class_destroy用于删除一个class,实际上其内部只是简单调用了class_unregister(cls)来注销cls */
void class_destroy(structclass*cls); 一个class属性对应于/sys/class/class.name(class.name就是该class的名称)目录里的一个文件。通过这些文件, 可以向用户空间输出一些关于该class的信息,也可从用户空间获取到一些信息。
/* class属性结构体 */
struct class_attribute {
struct attribute attr;
/* 当用户空间读取该属性时,调用show函数输出一个"属性值"给用户空间 */
ssize_t (*show)(structclass*,char* buf);
/* 当用户空间写该属性时,调用store函数保存用户写入的"属性值" */
ssize_t (*store)(structclass*,constchar* buf,size_tcount);
};
struct attribute {
constchar * name;
struct module * owner;
mode_t mode;
};
/* CLASS_ATTR可以在编译时创建一个class属性,该属性的名称为class_attr_name */
#define CLASS_ATTR(_name,_mode,_show,_store) \
struct class_attribute class_attr_##_name = __ATTR(_name,_mode,_show,_store)
/* class_create_file与class_remove_file用于创建与删除class默认属性外的属性 */
int __must_check class_create_file(structclass*,
conststruct class_attribute *);
void class_remove_file(structclass*,conststruct class_attribute *);
2、class_device
一个class可以看成是一个容器(一个子系统subsystem),包含了很多的class_device,这些class_device是由class这个大的容器来管理的,而每个class_device都对应着一个具体的设备。
每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备并通过struct class_device中的dev成员(一个指向struct device的指针)关联一个物理设备。一个逻辑设备总是对应一个物理设备,而一个物理设备却可以对应多个逻辑设备。
实际上,class_device在/sys/class/subsystem生成的目录就是上面提到的class_device。这样第2点也有了。
/* class_device结构体 */ struct class_device {
struct list_head node; /* 仅供驱动核心内部使用 */
struct kobject kobj; /* 该class_device相应的kobject,仅供驱动核心内部使用 */
structclass *class; /* 该class_device所属的class,必须有 */
dev_t devt; /* 该class_device的设备编号,用于创建其dev属性文件,仅供驱动核心内部使用 */
struct device * dev; /* 指向该class_device相关的device结构体(物理设备),可选.若不为NULL,用于创建一个从class入口到/sys/devices 下相应入口的符号连接,以便用户空间查找设备入口 */
void * class_data; /* 该class_device的私有数据指针 */
struct class_device *parent; /* parent of this child device, if there is one */
struct attribute_group ** groups; /* optional groups */
void (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);
char class_id[BUS_ID_SIZE]; /* 该class_device的名称,在其所属class中应是唯一的,不可重名 */
};
/* class_devic注册函数 */
int __must_check class_device_register(struct class_device *);
void class_device_unregister(struct class_device *); 与class一样,建立一个class_device也有两种方法
a、根据需要,填充一个struct class_device,然后再调用class_device_register注册该class_device就ok了(此法比较灵活,可以自己定制很多东西)
b、就是通过下面的class_device_create来创建一个class_device,该函数会返回一个指向刚建立的class_device的指针(创建class_device的最简单方法)
/* class_device_create用于创建一个class_device,其名称最后两个参数决定(类似于printf的格式化字符串)
* cls指明了其所属的class,可以是自己填充的class或由class_create返回的class
* parent指明了该class_device的父class_device,若没有,则为NULL
* 该class_device的设备编号,用于创建其dev属性文件,必须指明
* device指明了该class_device(逻辑设备)对应的device(物理设备),可有可无,无则为NULL
* 实际上,class_device_create也就是填充一个class_device,然后调用了class_device_register注册该class_device
*/
struct class_device *class_device_create(structclass*cls,
struct class_device *parent,
dev_t devt,
struct device *device,
constchar*fmt,...)
__attribute__((format(printf,5,6)));
/* class_destroy用于删除一个class,内部调用了class_unregister(cls)来注销cls */
void class_device_destroy(structclass*cls, dev_t devt); class_device属性对应于/sys/class/class.name/class_device.class_id目录下一个文件。通过这些 文件,可以向用户空间输出一些关于该class_device的信息,也可从用户空间获取到一些信息。
/* class_device属性,其show和store函数类似于class属性的show和store函数 */
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *,char* buf);
ssize_t (*store)(struct class_device *,constchar* buf,size_tcount);
};
/* CLASS_DEVICE_ATTR可以在编译时创建一个class_device属性,该属性的名称为class_device_attr_name */
#define CLASS_DEVICE_ATTR(_name,_mode,_show,_store) \
struct class_device_attribute class_device_attr_##_name = \
__ATTR(_name,_mode,_show,_store)
/* class_device_create_file与class_device_remove_file用于创建与删除class_device默认属性外的属性 */
int __must_check class_device_create_file(struct class_device *,
conststruct class_device_attribute *);
void class_device_remove_file(struct class_device * class_dev,
conststruct class_device_attribute * attr); 其实,在调用class_device_register注册一个class_device时,该函数内部调用了
int class_device_add(struct class_device *class_dev)
在class_device_add内,通过class_device_create_file创建dev、uevent和该class_device 所拥有的默认属性(由class_device.class->class_dev_attrs指定)等属性文件。这样第3点也有了。
dev属性文件用于向用户空间输出该class_device的设备编号。
uevent属性文件使用户可以手动触发uevent事件(通过向该文件写,如add、remove等字符串)。
接下来是两个基于mdev的驱动例子。
struct class_interface {
struct list_head node;
struct class *class; /* 该class_interface所属的class */
int (*add) (struct class_device *, struct class_interface *); /* class属性 */
void (*remove) (struct class_device *, struct class_interface *); /* class属性 */
int (*add_dev) (struct device *, struct class_interface *);
void (*remove_dev) (struct device *, struct class_interface *);
};
/* class_interface注册函数 */
int __must_check class_interface_register(struct class_interface *);
void class_interface_unregister(struct class_interface *);
转自:http://hi.baidu.com/ss0ss70/blog/item/b62aaeef4376a6dd2f2e21a1.html
mdev的基本工作原理【转】的更多相关文章
- 菜鸟学Struts2——Struts工作原理
在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...
- 【夯实Nginx基础】Nginx工作原理和优化、漏洞
本文地址 原文地址 本文提纲: 1. Nginx的模块与工作原理 2. Nginx的进程模型 3 . NginxFastCGI运行原理 3.1 什么是 FastCGI ...
- HashMap的工作原理
HashMap的工作原理 HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...
- 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)
RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- Servlet的生命周期及工作原理
Servlet生命周期分为三个阶段: 1,初始化阶段 调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...
- 代码管理工具 --- git的学习笔记二《git的工作原理》
通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...
- 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL
周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...
- 浏览器内部工作原理--作者:Tali Garsiel
本篇内容为转载,主要用于个人学习使用,作者:Tali Garsiel 一.介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到 ...
随机推荐
- awk速查手册
简介awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进 ...
- HDU 6153 KMP
最终刷KMP目标就是为了挑战这道题!现在成功了恩... 首先,题目大意是:给出一个字符串str1,之后给出另一个字符串str2,问,str2的后缀在str1匹配的次数*后缀当前长度是多少 首先考虑正统 ...
- datatime来计算代码段运行时长
小知识点:编程中一般都是先乘后除,这样结果更为精确 先定义 DataTime startTime = DataTime.Now; 中间是运行代码 最后TimeSpan ts = DataTime.No ...
- 当我们在讨论CQRS时,我们在讨论些神马?
当我写下这个标题的时候,我就有些后悔了,题目有点大,不太好控制.但我还是打算尝试一下,通过这篇内容来说清楚CQRS模式,以及和这个模式关联的其它东西.希望我能说得清楚,你能看得明白,如果觉得不错,右下 ...
- luogu2023 [AHOI2009]维护序列
线段树加乘懒标记裸题. #include <iostream> #include <cstdio> using namespace std; typedef long long ...
- 项目管理者必知:适用于仪表盘项目的7个优秀JavaScript库
仪表盘是用于目标或业务流程的视觉指示工具,也用于切割杂乱无章的数据,从而分割出要点的重要工具.它可以帮助评估信息并及时做出正确的决定,一款实时可视化的仪表盘通常由图标.测绘图.图形符号以及数据表格等组 ...
- 友推在Android 实现微信等分享代码的常见问题
介绍,最近 做了一个项目,需要集成分享功能.果断选择 友推. 集成过程,参考友推官方提供的集成文档即可 废话不多说,主要说一下自己在集成过程中遇到的一些问题,主要有两个: 问题1. 引入youtui- ...
- github 上传项目
注册账户以及创建仓库 要想使用github第一步当然是注册github账号了.之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之 ...
- Windows命令行中pip install jieba,但没有安装到anaconda3中
系统混淆了python3环境下的pip和anaconda3环境下的pip. 找到Anaconda3的Scripts目录,我这里是C:\Users\Diane\Anaconda3\Scripts 将该目 ...
- c++ 中double与string之间的转换,char *
运行代码为 /* * main.cpp * * Created on: Apr 7, 2016 * Author: lizhen */ #include <iostream> //#inc ...