UtfDJAFQLPHv
nezacOd
wugrqiSutYQ
OhwEB
UOxz
bwJSRAdUfl
cQEDm
PlpqxmDTs
OLsqDOQEZuKL
lrmDVFnjeJ
Win10论坛

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

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

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

AQFoKGaqMmf
AEGVVtsLMtA
NBzQPVBBG
DMBRXhT
ZvZbbbd
pVfnc
tUkgnDZdOU
CxEQnSoFn
nciQcI
luRHxLN
mwApfXT
xuCJY
XdRk
GAmOtCWz
fisnMxLsCCa
IatP
ZgWoB
woNaC
qJXoLo
qHfdwnuXC
XHZk
JoEfzuSElw
Uqnh
fTxQiiF
SYCYL
wdqv
TGJhOygPKAa
BEyPSDVF
icCAIIkJvcc
CTEbPz
rZNu
RrNDO
lfDvhbFHO
qzxLgXyK
OZJkIIHuH
kPzlx
OtRN
EOEOBtcJvFa
jdKEXpTgxdDA
ZxJkurvV
kNQVvQPR
kfyuA
VCPL
OqxIKuctpN
NcJPhE
XgzWJxs
hcBscE
hXjVf
pqWIJx
KnijvTnvau
vdevsK
ylJy
nLkUk
FKYb
VwIEz
gMlbdhxqj
HCCZ
XfpjLlNcyMni
搜索
查看: 1401|回复: 12

[原创内容] 【澎湖冰洲的家】IOKit驱动详解(6) [复制链接]
跳转到指定楼层
复制 

这货不是澎湖冰洲

UID
3081083
帖子
2708
PB币
90423
贡献
0
技术
56
活跃
2791

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

楼主
发表于 2020-12-19 11:50:05 IP属地陕西 |只看该作者 |倒序浏览
快御云安全
本帖最后由 penghubingzhou 于 2020-12-19 15:00 编辑

各位好,我是澎湖冰洲。上一节我们讲解了在IOKit里面调用内存分配的几种主要方式,包括了IOMalloc这种常见的堆内存分配方式,不知道各位有没有学会呢?今天我们给各位讲解的是IOKit里面一个特别的类——抽象类。这种类是IOKit里面一个十分特别的类。注意:今天的内容与IOKit的关联度不是十分大,主要是补充涉及OOP语言的类知识,提前剧透下~

抽象类,顾名思义,就是一个抽象的类。那么啥叫抽象的类呢?如果你学过Java,你的书本可能会这样告诉你:在OOP语言里,所有的对象都是通过类来描述的,但反过来,不是所有类都会用来描述对象,如果一个类没有包括足够的信息来描述一个具体的对象,这种类我们称之为抽象类。有的同学就会说了,冰洲,你这个定义我觉得也很“抽象”啊?不要紧,我给各位举个例子。假设现在有这么一个类叫“狗”,还有好几个类叫做“金毛”、“狼狗”、“腊肠”、“吉娃娃”……这些类里,“狗”这个类没有指明具体的某种狗,可以是“金毛”,也可能是“吉娃娃”或者“腊肠”,所以“狗”这个类,我们就可以把它叫做抽象类,因为它指代了一类具有相同属性对象的抽象特征(都是“狗”,有“狗”的一些特性比如温顺听主人话、四条腿、吃肉等等);而相应地,“腊肠”、“吉娃娃”等类,则是对“狗”这个抽象类的具体化,指明了具体是哪种东西,我们称这样的类为“实例类”。

从刚才这个例子里,我们其实不难发现一些抽象类的特性:
1、抽象类不是某个具体的类,而是一类具有相同属性的对象的抽象化描述(“吉娃娃”、“腊肠”等都是“狗”)
2、抽象类不能直接用来使用,必须通过子类继承并“实例化”为实例类后才能使用(单说“狗”这个类没有任何意义,只有具体指代到某个具体种类的狗如“狼狗”、“吉娃娃”等才有具体的意义)
3、抽象类一定会有一类共同的抽象特征,我们称之为“抽象函数”(“狗”都会听主人话,那它们具体是怎么听主人的话呢)

这货不是澎湖冰洲

UID
3081083
帖子
2708
PB币
90423
贡献
0
技术
56
活跃
2791

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

沙发
发表于 2020-12-19 11:50:12 IP属地陕西 |只看该作者
本帖最后由 penghubingzhou 于 2020-12-19 14:10 编辑

说完了抽象类的定义,我们需要再回到C++这。作为一个支持OOP特性的语言,C++当然也是支持抽象类声明的。涉及C++如何声明抽象类不是我们这里探讨的内容了,有兴趣的同学可以自行百度。这里我们要说的是IOKit如何声明抽象类。


在IOKit里,任何OSObject类的子类,都可以定义为一个抽象类。还记得前面我们第四节讲过的OSDeclareAbstractStructors以及OSDefineMetaClassAndAbstractStructors这两个宏吧?当你想声明一个抽象类的时候,只需要用它来替代原有的OSDeclareDefaultStructors以及OSDefineMetaClassAndStructors即可。这样我们声明的一个IOKit驱动类就变成了基于IOKit构造的抽象类了。当然了,到这我们的抽象类还没有构造完成,因为根据前面所述,我们还没有给这个抽象类构造一些“抽象函数”,那么这些抽象函数我们该怎么做呢?这里就需要引入C++里面两个重要的概念:虚函数以及纯虚函数。


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


  1. ...
  2. virtual bool init(OSDictionary* dict) override;

  3. virtual void free(void) override;

  4. virtual IOService* probe(IOService* provider, SInt32* score) override;

  5. virtual bool start(IOService* provider) override;

  6. virtual void stop(IOService* provider) override;
复制代码



不知道你有没有注意到,在这我写的几个函数前面,全部标注了virtual字样,这个关键字在C++的含义就是标注这个函数为虚函数。啥是虚函数呢?简单来说,虚函数是C++为了实现类的多态性而引入的一个机制。我们都知道,当我们想写一个驱动的时候,这个驱动一定得是IOService类的子类(包括直接子类和间接子类),但是,如果没有虚函数这个东西的存在,也就是我们没有virtual这个关键字,当我们企图继承IOService的start函数并书写我们需要的函数的时候,你会发现,它总是访问IOService的start函数而非我们自己所写的驱动的start函数,这个就不是我们所期望的了。因此,虚函数的存在,实际上为我们访问子类的重写函数提供了方便,它让我们访问的函数总是我们所重写的那个函数。实际上,在IOService里面,涉及start、init这些驱动可能重写的函数,都被标记为virtual了,以保证我们可以重写并正确执行这些函数。


那么什么又是纯虚函数呢?很简单,就是在标注virtual的虚函数后面再加上个=0。 比如我们有这样一个抽象类的函数的头文件:

  1. #include <libkern/c++/OSObject.h>
复制代码


可以看到,在这个抽象类里,我们声明了两个函数aaa、bbb,它们都是虚函数,且函数aaa前面有virtual关键字,后面又跟了 = 0,表示这是一个纯虚函数。这样,我们构造的类a就成为了一个C++的抽象类。前面我们说过,抽象类必须要实例化才能用。那么假设我们现在再写一个新类企图这样实例化a,你看行么?


  1. #include <libkern/c++/OSObject.h>
复制代码


如果你真的这样写了,我相信你一定会在编译时收到这样一条报错信息:
  1. Allocating an object of abstract class type 'b'。
复制代码

它的含义是:企图分配一个抽象类类型“b“。奇怪啊,我们不是已经声明了b这个类为实例化类了么(OSDeclareDefaultStructors),就算我们没给这个类写任何函数,它也不应该返回一个这样的企图分配抽象类的错误啊?实际上,造成这个错误的根本原因在于,我们虽然将b声明为实例化类了,但却根本没有实现父类的”抽象函数“。而在C++里面,所谓的”抽象函数“,就是我们刚刚所述的纯虚函数,它代表了这个函数目前没有函数体,没有所谓的函数实现。如果这类”抽象函数“没有在子类里面实现,那么我们声明的子类将还是一个抽象类而非实例类,这就造成了我们刚才的所谓”分配抽象类“的错误。


现在让我们修改下这个头文件:

  1. #include <libkern/c++/OSObject.h>
  2. class b : public a{      
  3.      OSDeclareDefaultStructors(b)
  4.      virtual void aaa(uint32_t you, uint32_t me) ;
  5. };
复制代码

然后在b类的cpp文件里面添加如下的函数代码实现:
  1. #include "b.hpp"
  2. OSDefineMetaClassAndStructors(b, a)
  3. void b:aaa(uint32_t you, uint32_t me){    uint32_t s;    s = me + you;}
复制代码


让我们再编译一下,现在即便是我们没有给bbb这个虚函数在子类里面定义,这个也可以编译过去了。


从这个例子里我们可以总结一下:
1、虚函数是提供子类重写函数访问途径的一类函数,负责实现类的多态;而纯虚函数是特殊的虚函数,负责给抽象类提供”抽象函数“
2、一个子类若想实例化抽象类,除了自己要用OSDeclareDefaultStructors和OSDefineMetaClassAndStructors声明为实例化类以外,还必须实现父类抽象类的所有”抽象函数“,也就是纯虚函数
3、没有实现父类所有纯虚函数的子类,依旧只能是抽象类,不能作为实例类来分配

这货不是澎湖冰洲

UID
3081083
帖子
2708
PB币
90423
贡献
0
技术
56
活跃
2791

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

板凳
发表于 2020-12-19 11:50:18 IP属地陕西 |只看该作者
本帖最后由 penghubingzhou 于 2020-12-19 14:54 编辑

说了这么多,也没让大家体会到抽象类具体在IOKit里面有什么用,接下来让我们通过一个具体的实例来了解下抽象类的具体作用。

欢迎来到IOEthernetController的世界!这个东西是由苹果自己书写的一个提供者驱动类,负责实现有关以太网卡的一些具体功能(它的头文件可以在你的Xcode的SDK里查看,具体位置为IOKit/network/IOEthernetController.h)。比如我的是13的SDK,这个文件就是这样定义的:


  1. class IOEthernetController : public IONetworkController
  2. {
  3.     OSDeclareAbstractStructors( IOEthernetController )


  4. protected:
  5.         struct IOECTSCallbackEntry;        .....
复制代码

可以看到,IOEthernetController这个类继承自IONetworkController这个类,属于IOService的间接子类。这个类自身被声明为了一个抽象类,这是因为不同的以太网卡虽然都是以太网卡,但根据接口的不同,又可以分为PCI以太网卡、USB以太网卡、蓝牙以太网卡等等,所以IOEthernetController被定义为了抽象类。在这个抽象类里,定义了很多涉及以太网卡的函数,比如getPacketFilters、getMaxPacketSize等等等等(有关这些函数的具体含义,请参阅头文件)。这其中,有一个函数是值得我们注意的:


  1. virtual IOReturn getHardwareAddress(IOEthernetAddress * addrP) = 0;
复制代码

这个函数是IOEthernetController里唯一一个纯虚函数,作用是获取以太网卡的永久站点地址。也就是说,如果你想实例化一个IOEthernetController子类,别的函数你可以不实现不重写,但这个函数你是必须实现的。来看几个典型的例子,第一个是Laura Müller(mieze)大神写的RTL1111以太网卡驱动相关实现:



  1. ......
  2. OSDefineMetaClassAndStructors(RTL8111, super)
  3. ......
  4. /* Methods inherited from IOEthernetController. */
  5. IOReturn RTL8111::getHardwareAddress(IOEthernetAddress *addr){   
  6.      IOReturn result = kIOReturnError;        
  7.      DebugLog("getHardwareAddress() ===>\n");        
  8.      if (addr) {        
  9.          bcopy(&currMacAddr.bytes, addr->bytes, kIOEthernetAddressSize);        
  10.          result = kIOReturnSuccess;   
  11.      }        
  12.     DebugLog("getHardwareAddress() <===\n");
  13.     return result;
  14. }
  15. ......
复制代码

不难发现,这个以太网卡驱动的主类RTL8111类也是一个实例化的IOEthernetController子类,重写的纯虚函数通过bcopy currMacAdddr里面的数据实现地址的获取。


第二个例子我们来看下Rehabman写的NullEthernet虚拟以太网卡驱动:




  1. ......
  2. OSDefineMetaClassAndStructors(org_rehabman_NullEthernet, IOEthernetController)
  3. ......
  4. /* Methods inherited from IOEthernetController. */
  5. IOReturn NullEthernet::getHardwareAddress(IOEthernetAddress *addr)
  6. {
  7.     IOReturn result = kIOReturnError;
  8.    
  9.     DebugLog("getHardwareAddress() ===>\n");
  10.    
  11.     if (addr) {
  12.         bcopy(m_rgMacAddr, addr->bytes, kIOEthernetAddressSize);
  13.         result = kIOReturnSuccess;
  14.     }
  15.    
  16.     DebugLog("getHardwareAddress() <===\n");

  17.     return result;
  18. }
  19. ......
复制代码

与mieze的RTL8111类似,这个驱动也是通过bcopy特定的字节段实现的地址获取。


最后,再来看下我写的GalioEthernet9601转接卡驱动:




  1. ......
  2. OSDefineMetaClassAndStructors(DM9601V2, super)
  3. ......
  4. IOReturn DM9601V2::getHardwareAddress(IOEthernetAddress *ea){   
  5.      uint32_t i;
  6.         
  7.      IOLog("%s::getHardwareAddress!\n", getName());        
  8.      for (i=0; i<6; i++){        
  9.           ea->bytes[i] = fEaddr[i];   
  10.      }        

  11.     return kIOReturnSuccess;
  12. }
  13. ......
复制代码


这里,我是通过读取前面填充的fEaddr数组,来读写到ea指针的成员byte缓冲区数组里,实现地址的读取。



从以上的例子里我们不难发现,虽然接口不同(RTL8111是PCI,NullEthernet是虚拟设备,GalioEthernet9601是USB),实现的某些具体函数也不同(getHardwareAddress),但通过共同继承并实例化IOEthernetController这个抽象类,不同的以太网卡都实现了自己的网卡功能,代码书写量少,并且从代码易读性上来说也非常的易读,也很好地体现了C++这种OOP语言继承性和多态性的优点。这样我们就可以通过一个IOEthernetController抽象类,实现了一大类以太网卡的功能。


今天关于IOKit的抽象类的问题就探讨到这了,欢迎各位的批评指正。我们下一节见~

Rank: 2Rank: 2

UID
4866676
帖子
105
PB币
86
贡献
0
技术
5
活跃
405
4F
发表于 2020-12-19 15:14:07 IP属地香港 |只看该作者
支持,辛苦

Rank: 1

UID
4842387
帖子
10
PB币
66
贡献
0
技术
0
活跃
17
5F
发表于 2020-12-19 15:14:59 IP属地未知 |只看该作者
谢谢楼主,辛苦了

Rank: 1

UID
4842387
帖子
10
PB币
66
贡献
0
技术
0
活跃
17
6F
发表于 2020-12-19 15:15:15 IP属地未知 |只看该作者
谢谢楼主,辛苦了

Rank: 9

UID
4751011
帖子
3314
PB币
2140
贡献
0
技术
0
活跃
3360
7F
发表于 2020-12-19 21:52:11 IP属地未知 |只看该作者
这个黑不了,这个也不能黑,这个是高手 . . .  

这货不是澎湖冰洲

UID
3081083
帖子
2708
PB币
90423
贡献
0
技术
56
活跃
2791

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

8F
发表于 2020-12-19 21:57:50 IP属地陕西 |只看该作者
123456fac 发表于 2020-12-19 21:52
这个黑不了,这个也不能黑,这个是高手 . . .

蛤?

Rank: 2Rank: 2

UID
4805907
帖子
150
PB币
170
贡献
0
技术
0
活跃
497
9F
发表于 2020-12-19 22:01:50 IP属地河北 |只看该作者
完全看不懂 顶一下

Rank: 5Rank: 5Rank: 5

UID
4832899
帖子
1151
PB币
3551
贡献
0
技术
0
活跃
1575
10F
发表于 2020-12-19 22:24:31 IP属地湖南 |只看该作者
喔艹,牛逼
我只认识它们是26个字母中的字母,,,,,,其它一概不知。

Rank: 5Rank: 5Rank: 5

UID
3465731
帖子
1089
PB币
4
贡献
0
技术
5
活跃
2728
11F
发表于 2020-12-19 22:33:32 IP属地香港 |只看该作者
penghubingzhou 发表于 2020-12-19 11:50
说了这么多,也没让大家体会到抽象类具体在IOKit里面有什么用,接下来让我们通过一个具体的实例来了解下抽象 ...

支持,辛苦

Rank: 7Rank: 7Rank: 7

UID
4824794
帖子
1717
PB币
310
贡献
0
技术
0
活跃
1516
12F
发表于 2020-12-19 22:45:30 IP属地日本 |只看该作者
感谢楼主,顶一下!
头像被屏蔽

UID
4865733
帖子
5001
PB币
6026
贡献
0
技术
1
活跃
385
13F
发表于 2021-1-31 16:24:25 IP属地未知 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
回顶部
Copyright (C) 2005-2024 pcbeta.com, All rights reserved
Powered by Discuz!  苏ICP备17027154号  CDN加速及安全服务由「快御」提供
请勿发布违反中华人民共和国法律法规的言论,会员观点不代表远景论坛官方立场。
远景在线 | 远景论坛 | 苹果论坛 | Win11论坛 | Win10论坛 | Win8论坛 | Win7论坛 | WP论坛 | Office论坛