本帖最后由 penghubingzhou 于 2020-12-19 14:54 编辑
说了这么多,也没让大家体会到抽象类具体在IOKit里面有什么用,接下来让我们通过一个具体的实例来了解下抽象类的具体作用。
欢迎来到IOEthernetController的世界!这个东西是由苹果自己书写的一个提供者驱动类,负责实现有关以太网卡的一些具体功能(它的头文件可以在你的Xcode的SDK里查看,具体位置为IOKit/network/IOEthernetController.h)。比如我的是13的SDK,这个文件就是这样定义的:
- class IOEthernetController : public IONetworkController
- {
- OSDeclareAbstractStructors( IOEthernetController )
- protected:
- struct IOECTSCallbackEntry; .....
复制代码
可以看到,IOEthernetController这个类继承自IONetworkController这个类,属于IOService的间接子类。这个类自身被声明为了一个抽象类,这是因为不同的以太网卡虽然都是以太网卡,但根据接口的不同,又可以分为PCI以太网卡、USB以太网卡、蓝牙以太网卡等等,所以IOEthernetController被定义为了抽象类。在这个抽象类里,定义了很多涉及以太网卡的函数,比如getPacketFilters、getMaxPacketSize等等等等(有关这些函数的具体含义,请参阅头文件)。这其中,有一个函数是值得我们注意的:
- virtual IOReturn getHardwareAddress(IOEthernetAddress * addrP) = 0;
复制代码
这个函数是IOEthernetController里唯一一个纯虚函数,作用是获取以太网卡的永久站点地址。也就是说,如果你想实例化一个IOEthernetController子类,别的函数你可以不实现不重写,但这个函数你是必须实现的。来看几个典型的例子,第一个是Laura Müller(mieze)大神写的RTL1111以太网卡驱动相关实现:
- ......
- OSDefineMetaClassAndStructors(RTL8111, super)
- ......
- /* Methods inherited from IOEthernetController. */
- IOReturn RTL8111::getHardwareAddress(IOEthernetAddress *addr){
- IOReturn result = kIOReturnError;
- DebugLog("getHardwareAddress() ===>\n");
- if (addr) {
- bcopy(&currMacAddr.bytes, addr->bytes, kIOEthernetAddressSize);
- result = kIOReturnSuccess;
- }
- DebugLog("getHardwareAddress() <===\n");
- return result;
- }
- ......
复制代码
不难发现,这个以太网卡驱动的主类RTL8111类也是一个实例化的IOEthernetController子类,重写的纯虚函数通过bcopy currMacAdddr里面的数据实现地址的获取。
第二个例子我们来看下Rehabman写的NullEthernet虚拟以太网卡驱动:
- ......
- OSDefineMetaClassAndStructors(org_rehabman_NullEthernet, IOEthernetController)
- ......
- /* Methods inherited from IOEthernetController. */
- IOReturn NullEthernet::getHardwareAddress(IOEthernetAddress *addr)
- {
- IOReturn result = kIOReturnError;
-
- DebugLog("getHardwareAddress() ===>\n");
-
- if (addr) {
- bcopy(m_rgMacAddr, addr->bytes, kIOEthernetAddressSize);
- result = kIOReturnSuccess;
- }
-
- DebugLog("getHardwareAddress() <===\n");
- return result;
- }
- ......
复制代码
与mieze的RTL8111类似,这个驱动也是通过bcopy特定的字节段实现的地址获取。
最后,再来看下我写的GalioEthernet9601转接卡驱动:
- ......
- OSDefineMetaClassAndStructors(DM9601V2, super)
- ......
- IOReturn DM9601V2::getHardwareAddress(IOEthernetAddress *ea){
- uint32_t i;
-
- IOLog("%s::getHardwareAddress!\n", getName());
- for (i=0; i<6; i++){
- ea->bytes[i] = fEaddr[i];
- }
- return kIOReturnSuccess;
- }
- ......
复制代码
这里,我是通过读取前面填充的fEaddr数组,来读写到ea指针的成员byte缓冲区数组里,实现地址的读取。
从以上的例子里我们不难发现,虽然接口不同(RTL8111是PCI,NullEthernet是虚拟设备,GalioEthernet9601是USB),实现的某些具体函数也不同(getHardwareAddress),但通过共同继承并实例化IOEthernetController这个抽象类,不同的以太网卡都实现了自己的网卡功能,代码书写量少,并且从代码易读性上来说也非常的易读,也很好地体现了C++这种OOP语言继承性和多态性的优点。这样我们就可以通过一个IOEthernetController抽象类,实现了一大类以太网卡的功能。
今天关于IOKit的抽象类的问题就探讨到这了,欢迎各位的批评指正。我们下一节见~ |