uuRRSZjyl
lJaiqMruGmxp
iYoS
XdcEV
wKnkvQguTRu
ugdfjibySn
YQHBvd
DhYdQaB
MltD
Win10论坛

Win10正式版系统下载主题平板

重定义Modern UI,打造完美Windows全新体验

Windows10下载|安装|新手宝典|必备软件

xzvus
HzuRsUy
BwvsuiUxEKL
AozucqzJn
PHNFFQF
NvzsLGk
VufgxCCW
UcdwpszlqzW
hjjJKvFjRq
hFsDANNpActU
OeyARxWegST
RzAr
tDaUGSp
dvBkXt
RLeWKmv
MKNnpYupkQb
XUfg
fUTbIF
tlhgK
yUodeT
TTFtyLVTX
lxgtshkKkXGm
CYFqMejZXA
UDxBLcheba
TAKhplNc
Jhcjs
srYIWmjO
pTNhC
fMNfGNcCay
RfVG
dfit
gHelvNmG
Swvk
bcmWKhCldbi
ztuaRGYNlS
jGrzfFIfuz
OACfSshxWz
xYsEaWKDhk
ymbNt
duUeWqW
KKJSaA
SbARBjvm
aCBcPanrWnRK
Ifif
UCmOKWpVjnw
UYIbc
kCzFXzo
rtujNYA
JgTRCmxcHqB
VYHgSN
VWCMQnsGM
HiItgtuvuphP
vspptNJ
RRbjKc
imhOXLhyFnT
KvFG
KgTc
huKt
搜索
查看: 3389|回复: 40

[教程] 【澎湖冰洲的家】IOKit驱动详解(2) [复制链接]

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-13 12:27:51 IP属地黑龙江 |显示全部楼层
快御云安全
本帖最后由 penghubingzhou 于 2020-4-14 14:23 编辑

大家好,我是澎湖冰洲。上篇帖子写到了我们的第一个IOKit驱动helloworld。不知道试验的各位有没有成功加载呢?反正我是成功了~上一节也留下来了一些疑问,这节我们来一一解决它们。

首先是第一个问题,我们的驱动究竟做了什么。从控制台的输出看,我们似乎是只输出了几个简单的信息,并没有做什么啊。实际上,驱动做的事情,要比我们想象的,复杂得多得多得多。下面,让我们分析上面教程所写的代码,进行逐行的分析。


首先先看helloworld.hpp。学过c++/c的同学应该清楚,hpp/h文件,也叫头文件(header file),是定义一个程序基础函数的地方。通过头文件,可以实现多个c/cpp文件之间的功能以及数据共享。这里,我们的程序只有这一个头文件。看第一二行代码:

  1. #include <IOKit/IOService.h>
  2. #include <IOKit/IOLib.h>
复制代码

这两个头文件,是编写IOKit的基础头文件,在我们编写驱动时,需要引入相关的文件,才能获得IOKit框架支持。

在引入之后,我们声明了一个类(class),它叫Hello_World, 是一个继承自IOService类(IOService本质是一个OSMetaClass的子类,有关OSMetaClass各类特性我将在后面的章节一一讲述)的子类。而这个Hello_World类,最终也将对应到IORegistry(IO注册表,可以用IORegistryExplorer软件查看,经常折腾黑果的人,对这个应该不会很陌生吧)的一个条目,也就是我们所写的驱动。


  1. class Hello_World : public IOService{
  2.      OSDeclareDefaultStructors(Hello_World);
  3.      
  4.     ..............
  5. };
复制代码

紧跟这个类声明的,是一个构造器与析构器声明。在c++里,一个类是需要一个构造器以及一个析构器的,这个构造器承担了这个类的入口模块功能,而析构器则承担出口功能。我们这里,使用的是IOKit给我们提供的默认构造器,它将为我们的Hello_World类提供一个默认的系统构造器。(注意:构造器声明必须紧跟在类声明的下一行)那么,对于一个OSMetaClass的子类而言,它应该按照怎么一个流程执行呢?按照IOKit的文档说明,我们可以了解到它的执行流程遵照这样一个顺序:




按照图中的顺序,在一个驱动的生命周期里,一个OSMetaClass的子类会执行这么多的函数。我们不难发现,除了probe()函数以外,它们都是成对出现的,声明了一个,就必然会声明另一个。其中,我们必须会写的成对函数是init()与free(),以及start()与stop()。而probe()函数虽然不成对出现,但也是必要的函数。所以,我们需要在我们这个驱动里,声明并实现相应的函数。

  1. ...
  2.     virtual bool init(OSDictionary* dict) override;
  3.    
  4.     virtual void free(void) override;
  5.    
  6.     virtual IOService* probe(IOService* provider, SInt32* score) override;
  7.    
  8.     virtual bool start(IOService* provider) override;
  9.    
  10.     virtual void stop(IOService* provider) override;
复制代码


注意,我们对相关函数的声明,要与父类(也就是IOService)相应的函数构造一致,并且要加上override关键字,不能出现偏差的情况。这是因为,我们声明的这些函数,是对父类函数的重载(面向对象概念,指继承父类的函数并且对其进行重写)。




到这里,我们的头文件就分析完了。接下来我们转入cpp文件,看下它的代码实现。

附件: 你需要登录才可以下载或查看附件。没有帐号?注册
4

查看全部评分

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-13 19:01:31 IP属地黑龙江 |显示全部楼层
本帖最后由 penghubingzhou 于 2020-4-15 09:19 编辑

cpp文件,是c++程序的主体部分,负责实现头文件的各类函数声明。在cpp里,我们首先引入了我们工程的头文件:
  1. #include "helloworld.hpp"

  2. #define super IOService

  3. ...
复制代码
值得注意的是这个super的宏定义,它定义了Hello_World类的父类IOService,这样作为子类的Hello_World,就可以去调用其父类的函数了。


随后,我们看到了又一个宏定义:
  1. OSDefineMetaClassAndStructors(Hello_World, IOService)
复制代码
这个宏定义,跟之前的OSDeclareDefaultStructors宏定义是一脉相承的。这里是在cpp文件里进行声明,引入头文件对类的构造,使其使用OSObject作为其基类(OSObject是所有IOKit驱动以及libkern定义的各种OS数据类型的超级父类)。它有两个参数,第一个声明你的驱动类,第二个则声明该驱动类的父类(也就是你在第二行定义的super类)。这个函数一定要放置在你即将实现的所有父类函数之前。




接下来,我们写了几个父类重载函数的实现:init()、probe()、start()、stop()以及free()。首先我们需要讲下这几个函数的意义。根据前面我贴的图,在这个驱动的生命周期里,这个驱动将按照如下顺序依次加载函数:


init():在调用一个驱动类的其他函数之前,这个函数必定被优先加载,它相当于c++的类构造器,负责驱动类的初始化操作。它只有一个参数,是info.plist中IOKitPersonalities信息的一个副本,返回值为布尔类型,如执行成功返回true,否则为false。在重载实现这个函数之前,我们必须用super::init()来实现父类的init函数

probe():这是IOKit驱动三重匹配机制之激活匹配所用到的模块。当该函数被执行时,将检查硬件设备。尽管它的参数provider是一个指向IOService的指针,但它也是可以通过动态转换来转换为与IOKitPersonalities中IOProviderClass类型相一致的指针的(这通过OSDynamicCast宏来实现,后面的内容会有讲述)。与init类似,重载实现probe函数时,应该首先使用super::probe()调用父类的probe函数,调用成功后,再执行对设备的任意检测。如果probe函数内实现的检测不能通过,那么函数执行失败,它将返回NULL,否则就应该返回一个IOService子类的实例对象,从而控制这个设备。通常来说,该函数都是返回当前的IOService实例(this关键字指针)。


start():如果probe函数已经成功执行,并且已通过三重匹配机制确认为最佳匹配驱动,那么此时,驱动将正式启动服务,此时驱动调用start函数。与前面两函数相似,重载这个函数之前,也需要先执行super::start()来执行父类的start函数,如果不成功,则放弃执行这个函数。start函数负责实现对硬件资源的分配,以及各种数据及资源的初始化。如果由于某些原因,这个函数无法正常完成初始化操作,并且已无法继续控制硬件时,此函数将返回false,然后继续调用三重匹配机制确定的第二优先级驱动控制设备。


stop():此函数是与start函数成对使用的函数,在设备移除或者驱动卸载时调用。当此函数调用时,应该释放掉所有start函数里初始化的资源以及分配的空间。在函数执行的最后,必须要调用超类的stop函数(super::stop())。


free():此函数是与init函数成对使用的函数,其作用类似c++的类析构器。此函数执行时,将释放掉在init函数内分配的资源以及空间,即便该驱动并未被作为最佳匹配也一样会执行。与stop函数类似,在使用的最后你需要调用父类的free函数(super::free)。

随后我们看到了每个函数里,使用了一个IOLog函数。这个函数是在IOKit驱动开发中常用的一个函数,对我们的驱动调试有着重大的作用。它的函数原型如下:

  1. IOLog (const char*);
复制代码
其中const char* 是一个指向char常量的指针。

IOLog的作用类似于C++的printf,只不过它输出的位置,在system.log里。由于IOKit驱动的设计不涉及任何GUI设计,且不能使用运行时的断点功能,因此,除了五国日志之外(五国日志是调试驱动故障的利器,下一节我们将着重介绍),IOLog几乎是我们获取此驱动运行信息的唯一手段,适当地使用IOLog,将有助于我们更加方便地调试驱动的问题所在。



1

查看全部评分

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-14 14:13:10 IP属地黑龙江 |显示全部楼层
本帖最后由 penghubingzhou 于 2020-4-14 14:17 编辑

目前我们对于我们的这个驱动的代码,已经有了一个初步的了解。下节我们将继续研究info.plist,看看这里面究竟又有什么“门道”,跟代码有什么关联,此外我还会讲解五国日志(也就是KP Log)怎样在驱动调试中发挥作用。敬请期待~

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-14 14:18:38 IP属地黑龙江 |显示全部楼层
自顶一下

Rank: 7Rank: 7Rank: 7

UID
695322
帖子
937
PB币
266
贡献
0
技术
85
活跃
1658
发表于 2020-4-14 14:20:46 IP属地贵州 |显示全部楼层
虽然看不懂,但也一定要支持高水平的原创作者。

Rank: 1

UID
2636793
帖子
82
PB币
19
贡献
0
技术
0
活跃
230
发表于 2020-4-14 14:21:03 IP属地内蒙古 |显示全部楼层
请教一下,free的调用是在设备卸载还是系统destory的时候?还是有回收的机制?

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-14 14:22:05 IP属地黑龙江 |显示全部楼层
fly3366 发表于 2020-4-14 14:21
请教一下,free的调用是在设备卸载还是系统destory的时候?还是有回收的机制?

free在设备卸载时调用,承担了一个最后销毁驱动对象的作用

Rank: 7Rank: 7Rank: 7

UID
4824794
帖子
1717
PB币
310
贡献
0
技术
0
活跃
1489
发表于 2020-4-14 14:28:59 IP属地浙江 |显示全部楼层
支持大佬!

Rank: 7Rank: 7Rank: 7

UID
4841127
帖子
1489
PB币
1191
贡献
0
技术
0
活跃
1788
发表于 2020-4-14 14:33:37 IP属地广东 |显示全部楼层
赶上直播了,教学很成功,谢谢督察。

Rank: 2Rank: 2

UID
4356868
帖子
280
PB币
385
贡献
0
技术
24
活跃
238
发表于 2020-4-14 14:43:33 IP属地广东 |显示全部楼层
谢谢楼主,我有点C++基础,来学一下看能不能学会

Rank: 2Rank: 2

UID
4034407
帖子
143
PB币
183
贡献
0
技术
0
活跃
289
发表于 2020-4-14 15:09:07 IP属地浙江 |显示全部楼层
观摩大佬!

Rank: 1

UID
4848188
帖子
113
PB币
668
贡献
0
技术
0
活跃
305
发表于 2020-4-14 15:10:04 IP属地上海 |显示全部楼层
添个瓦 期待孵化出更多大佬

我什么都不知道

Rank: 9

UID
2964367
帖子
1819
PB币
424
贡献
0
技术
0
活跃
396
发表于 2020-4-14 15:43:20 IP属地未知 |显示全部楼层
大神是否可以用一个开源驱动的源码,比如voodoops2controller的源码为例,讲解下工程如何构建出来。

Rank: 1

UID
4671004
帖子
75
PB币
106
贡献
0
技术
0
活跃
174
发表于 2020-4-14 16:24:26 IP属地广西 来自手机 |显示全部楼层
估计很多未接触的,会一头雾水,要有信心,耐心,细心,恒心。打个比方,如建大楼,库(各种公标的钢筋,水泥……)类(什么标号钢筋,什么种类水泥……)函数(钢筋用法,水泥配比……)。设备路径(在地球哪个旮旯建的房子,床铺摆放楼层位置方向……),硬件(床铺:硬板/弹簧单人床,硬板/弹簧双人床……),驱动(使用床铺睡大觉,吃得太胖,床铺承受不住,塌了)还好这床铺能记忆恢复,减肥成功又可以愉快的找周公去了!以上纯属虚构,支持楼主

嫉恶如仇,是非分明的冰境泽

UID
3081083
帖子
2708
PB币
88423
贡献
0
技术
56
活跃
2772

巡察使 7周年庆典勋章 我是大学生!

发表于 2020-4-14 18:04:29 IP属地黑龙江 |显示全部楼层
廿浮沉 发表于 2020-4-14 15:43
大神是否可以用一个开源驱动的源码,比如voodoops2controller的源码为例,讲解下工程如何构建出来。
{:5_2 ...

后面我会在发的时候插入一些已有代码的案例进行分析
1

查看全部评分

Rank: 5Rank: 5Rank: 5

UID
2428049
帖子
918
PB币
1277
贡献
0
技术
1
活跃
1427
发表于 2020-4-14 22:02:48 IP属地广东 |显示全部楼层
好帖子 赞!

Rank: 1

UID
4843609
帖子
21
PB币
0
贡献
0
技术
0
活跃
271
发表于 2020-4-14 22:55:36 IP属地广东 |显示全部楼层
看完了,催更

Rank: 1

UID
4861521
帖子
67
PB币
233
贡献
0
技术
0
活跃
194
发表于 2020-4-14 23:37:30 IP属地重庆 |显示全部楼层
好贴顶一个,话说这贴算入门吧.....感觉不是很好啃

Rank: 5Rank: 5Rank: 5

UID
4341387
帖子
567
PB币
458
贡献
0
技术
0
活跃
1194
发表于 2020-4-14 23:54:57 IP属地重庆 来自手机 |显示全部楼层
写一个走USB总线的触摸板的驱动吧

Rank: 2Rank: 2

UID
4111881
帖子
318
PB币
1365
贡献
0
技术
0
活跃
711
发表于 2020-4-15 00:06:20 IP属地陕西 |显示全部楼层
可以的,学习了
回顶部
Copyright (C) 2005-2024 pcbeta.com, All rights reserved
Powered by Discuz!  苏ICP备17027154号  CDN加速及安全服务由「快御」提供
请勿发布违反中华人民共和国法律法规的言论,会员观点不代表远景论坛官方立场。
远景在线 | 远景论坛 | 苹果论坛 | Win11论坛 | Win10论坛 | Win8论坛 | Win7论坛 | WP论坛 | Office论坛