应用与内核通信简概(C/C++内核编程)

应用与内核通信简概(C/C++)

  • 1.背景
  • 2.内核方面编程
    • 2.1 生成控制设备
    • 2.2 控制设备的名字和符号链接
    • 2.3 控制设备的删除
    • 2.4 分发函数(派遣函数)
    • 2.5 请求的处理
  • 3.应用方面编程
    • 3.1 基本的功能需求
    • 3.2 在应用程序中打开与关闭设备
    • 3.3 设备控制请求
    • 3.4 内核中对应处理

1.背景

  内核程序既没有窗口,也没有控制台,唯一使我们能够看到结果的是调试日志。那如果想要用户“看到”些什么,很多情况下,内核需要与应用层通信。一方面,一些软件的某些功能必须要通过内核程序才能实现,但是又必须在软件界面上有所展示;另一方面,有些内核程序的功能,需要提供方便用户来操作的手段,这又必须体现在软件界面上。

  为此,内核程序必须要用某种方式和应用软件互通信息。

2.内核方面编程

2.1 生成控制设备

  如果一个驱动需要和应用程序通信,那么首先要生成一个设备对象(DeviceObject),往往称之为控制设备对象

  设备对象是非常重要的元素。设备对象和派遣函数构成了整个内核体系的基本框架。设备对象可以在内核中暴露出来给应用层,应用层可以像操作文件一样操作它。

  生成控制设备可以使用函数:IoCreateDevice

示例代如下:

应用与内核通信简概(C/C++内核编程)_第1张图片

2.2 控制设备的名字和符号链接

  控制设备需要有一个名字,这样它才会被暴露出来,供其他程序打开与之通信。设备的名字可以在调用IoCreateDevice时指定。

  此外,应用层是无法直接通过设备的名字来打开对象的,为此必须要建立一个暴露给应用层的符号链接。

  生成符号链接的函数是:IoCreateSymbolicLink

示例代如下:

应用与内核通信简概(C/C++内核编程)_第2张图片
应用与内核通信简概(C/C++内核编程)_第3张图片

2.3 控制设备的删除

  在驱动中生成了控制设备及其符号链接,那么在驱动卸载时就应该删除它们;否则符号链接就会一直存在。应用程序还可能会尝试打开进行操作。

  依次删除符号链接控制设备即可。

  使用函数 IoDeleteSymbolicLink IoDeleteDevice

示例代如下:

应用与内核通信简概(C/C++内核编程)_第4张图片

2.4 分发函数(派遣函数)

  分发函数是一组用来处理发送给设备对象的请求的函数。这些函数由内核驱动的开发者编写,以便处理这些请求并返回给Windows。

  每个驱动都有一组自己的分发函数。Windows的IO管理器在收到请求时,会根据请求发送的目标,也就是一个设备对象,来调用这个设备对象所从属的驱动对象上对应的分发函数。

  不同的分发函数处理不同的请求。
应用与内核通信简概(C/C++内核编程)_第5张图片

  • 打开设备,会触发IRP_MJ_CREATE,
  • 关闭设备,会触发IRP_MJ_CLEANUP和IRP_MJ_CLOSE

2.5 请求的处理

  在分发函数中处理请求的第一步是获得请求的当前栈空间(Current StackLocation)。

  请求的当前栈空间可以用IoGetCurrentIrpStackLocation取得。利用当前栈空间指针来获得主功能号。每种请求都有一个主功能号来说明这是一个什么请求,然后可以根据主功能号做不同的处理。

●打开请求的主功能号是IRP_ MJ_ CREATE。
●关闭请求的主功能号是IRP_ MJ_ CLOSE。
●设备控制请求的主功能号是IRP_MJ_ DEVICE _CONTROL.

3.应用方面编程

3.1 基本的功能需求

前面的内容中介绍了在内核驱动中需要增加的部分。但是到此为止,都没有说明应用和内核之间要进行怎样的通信。

下面将举一个例子实现应用程序与内核通信,访问PCI配置空间、枚举系统内的所有PCI设备。

3.2 在应用程序中打开与关闭设备

  在应用程序中打开设备和打开文件没有什么不同,除了路径有点特殊。

  打开设备使用API函数 CreateFile

  文件的路径就是符号链接的路径,但是符号链接的路径在应用层看来,是以“\.\”开头的。注意,这些“\”在C语言中要使用“\”来转义,所以在C代码中,生成的符号链接就变成了这个样子。 L"\\.\HelloDDK"
应用与内核通信简概(C/C++内核编程)_第6张图片
  CreateFile中最重要的参数就是第一个,用一个字符串来表示设备的路径。

注意:如果失败了并不是返回NULL,而是返回INVALID_ HANDLE VALUE,
而且INVALID_ HANDLE VALUE并不是NULL。
这是一个实际写代码时容易出现隐藏错误的地方。

  关闭设备非常简单,调用函数CloseHandle即可。应用与内核通信简概(C/C++内核编程)_第7张图片

3.3 设备控制请求

  设备控制请求可以进行输入,也可以进行输出。无论输出还是输入都可以利用一个简单的自定义结构和长度缓冲区,所以开发者可以根据自己的需要来设计非常复杂的通信协议。

  在这个例子里做一个简单的设计:定义一个叫作“枚举PCI”的功能号。每个设备控制请求会有一个功能号,以便区分不同的设备控制请求。
应用与内核通信简概(C/C++内核编程)_第8张图片

 使用 DeviceIoControl 函数发送请求
在这里插入图片描述

3.4 内核中对应处理

目前的情况下,应用程序中调用DeviceIoControl定会返回错误,因为内核驱动中还没处理。

现在回到内核编程中来修改。在处理设备控制请求时,还有如下的任务要完成:
在这里插入图片描述
应用与内核通信简概(C/C++内核编程)_第9张图片

你可能感兴趣的:(驱动开发,c语言,c++)