6.3 一次性申请

在低资源环境下,内核中申请的资源很有可能失败,对于一些逻辑来说,一旦资源申请失败将无法继续往下执行,或者往下执行一定会蓝屏。请看下面这个例子:

请读者思考一下,当驱动需要卸载的时候,当前驱动所创建的线程是否必须停止呢?答案是肯定的,而很多开发者把停止线程的任务放到了驱动的卸载函数,即DriverUnload函数中,如果读者对此知识点有遗忘,请参考本书前面章节。

驱动卸载函数大概是下面这个样子:

关键的StopTaskThread实现如下:

上面的代码在绝大部分环境下工作良好,但是一旦发布此驱动,量级到达千万时,就会陆续收到蓝屏投诉。

观察上面的代码,使用了KeWaitForMultipleObjects函数来等待16个线程退出,而这个等待函数本身需要一个非分页的内存块,即代码中的pWaitBlockArrary,试想一下,一旦在非分页内存紧张的情况下,ExAllocatePoolWithTag函数分配失败,那么StopTaskThread函数实际并没有等待线程退出,而在DriverUnLoad函数中,对失败是不可容忍的,系统一旦调用DriverUnLoad函数,意味着驱动马上要被从内存中卸载,这个行为是不可取消的,如果线程没有退出或退出失败,驱动卸载后一定会发生蓝屏。

对于上面的例子,较好的解决方法是在DriverEntry中把KeWaitForMultipleObjects所需要的资源申请好,即“一次性申请”到位。通俗来讲,就是需要把这类情况全部梳理出来,这些内存或资源对于自身驱动来说是“性命攸关”的资源,应该放在DriverEntry中申请,一旦申请失败,驱动DriverEntry也应返回失败。

上面关于内存的例子是一个特例,请读者放眼于所有资源,因为对于系统来说,所有资源的申请都存在失败的概率。