penghubingzhou 发表于 2020-4-13 12:27

【澎湖冰洲的家】IOKit驱动详解(2)

本帖最后由 penghubingzhou 于 2020-4-14 14:23 编辑

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

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


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

#include <IOKit/IOService.h>
#include <IOKit/IOLib.h>
这两个头文件,是编写IOKit的基础头文件,在我们编写驱动时,需要引入相关的文件,才能获得IOKit框架支持。

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


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




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

...
    virtual bool init(OSDictionary* dict) override;
   
    virtual void free(void) override;
   
    virtual IOService* probe(IOService* provider, SInt32* score) override;
   
    virtual bool start(IOService* provider) override;
   
    virtual void stop(IOService* provider) override;

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




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

penghubingzhou 发表于 2020-4-13 19:01

本帖最后由 penghubingzhou 于 2020-4-15 09:19 编辑

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

#define super IOService

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


随后,我们看到了又一个宏定义:
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驱动开发中常用的一个函数,对我们的驱动调试有着重大的作用。它的函数原型如下:

IOLog (const char*);其中const char* 是一个指向char常量的指针。

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



penghubingzhou 发表于 2020-4-14 14:13

本帖最后由 penghubingzhou 于 2020-4-14 14:17 编辑

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

penghubingzhou 发表于 2020-4-14 14:18

自顶一下

youyafei 发表于 2020-4-14 14:20

虽然看不懂,但也一定要支持高水平的原创作者。

fly3366 发表于 2020-4-14 14:21

请教一下,free的调用是在设备卸载还是系统destory的时候?还是有回收的机制?

penghubingzhou 发表于 2020-4-14 14:22

fly3366 发表于 2020-4-14 14:21 https://www.pcbeta.com/static/image/common/back.gif
请教一下,free的调用是在设备卸载还是系统destory的时候?还是有回收的机制?

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

龙卷风05 发表于 2020-4-14 14:28

支持大佬!

CeWnHai 发表于 2020-4-14 14:33

赶上直播了,教学很成功,谢谢督察。

vincent915000 发表于 2020-4-14 14:43

谢谢楼主,我有点C++基础,来学一下看能不能学会

liujunhui520 发表于 2020-4-14 15:09

观摩大佬!

rooy1996 发表于 2020-4-14 15:10

添个瓦 期待孵化出更多大佬

廿浮沉 发表于 2020-4-14 15:43

大神是否可以用一个开源驱动的源码,比如voodoops2controller的源码为例,讲解下工程如何构建出来。
{:5_262:}

diaoni38 发表于 2020-4-14 16:24

估计很多未接触的,会一头雾水,要有信心,耐心,细心,恒心。打个比方,如建大楼,库(各种公标的钢筋,水泥……)类(什么标号钢筋,什么种类水泥……)函数(钢筋用法,水泥配比……)。设备路径(在地球哪个旮旯建的房子,床铺摆放楼层位置方向……),硬件(床铺:硬板/弹簧单人床,硬板/弹簧双人床……),驱动(使用床铺睡大觉,吃得太胖,床铺承受不住,塌了)还好这床铺能记忆恢复,减肥成功又可以愉快的找周公去了!以上纯属虚构,支持楼主

penghubingzhou 发表于 2020-4-14 18:04

廿浮沉 发表于 2020-4-14 15:43 https://www.pcbeta.com/static/image/common/back.gif
大神是否可以用一个开源驱动的源码,比如voodoops2controller的源码为例,讲解下工程如何构建出来。
{:5_2 ...

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

liaoyudong2 发表于 2020-4-14 22:02

好帖子 赞!

ailuoku 发表于 2020-4-14 22:55

看完了,催更

cyjyyd 发表于 2020-4-14 23:37

好贴顶一个,话说这贴算入门吧.....感觉不是很好啃

tyuan1231 发表于 2020-4-14 23:54

写一个走USB总线的触摸板的驱动吧

endy506 发表于 2020-4-15 00:06

可以的,学习了
页: [1] 2 3
查看完整版本: 【澎湖冰洲的家】IOKit驱动详解(2)