windows驱动开发-32位和64位

发布于:2024-05-16 ⋅ 阅读:(26) ⋅ 点赞:(0)

这部分其实是过时的知识点,毕竟win 11之已经不支持32位系统了,但是还是列出来吧。

32位主要是指x86体系,在早期,32位的应用程序和系统只支持4GB的内存地址寻址,这也是最大的特色之一,在那个时代,由于对内存限制导致出现了许多精巧的设计,这个体系也称为一个经典的体系,然而时代变了,64位出现之后,内存不再是一个问题,这也导致了后续很多编程都开始向64位转移,这篇文档主要侧重点也是讲32位驱动如何升级到64位驱动。

32位和64位的区别

32位和64位的本质区别就是指针长度的不一样,32位的指针是4字节,64位指针是8字节,大部分和指针长度无关的程序都能直接在64位下编译成功,唯一的例外是,用32位无符号整数和指针进行互相转换,这种情况下需要将32位无符号整数换成64位无符号整数。

对于用户模式应用程序,64 位 Windows 包括 Windows 上的 Windows (WOW64) thunking 层 ,使 32 位应用程序能够在 64 位版本的 Windows 上执行,虽然会有一定的性能下降。 它通过截获 32 位函数调用,并在转换到 64 位内核之前根据需要将指针精度参数类型转换为固定精度类型来执行此操作。 此转换过程称为 thunking。注意 此 thunking 仅适用于 32 位 应用程序;64 位版本的 Windows 不支持 32 位 驱动程序 。

在 32 位 Windows 上,整型、长整型和指针数据类型的大小都相同,即 32 位。 这种便捷的数据类型大小统一性对聪明的 C 程序员来说是一个福音,他们中的许多人都将其视为理所当然。

但是,在 64 位 Windows 上,这种统一性假设不再有效。 指针的长度现在为 64 位,但整数和长数据类型的大小仍与之前相同32 位。 这是因为,虽然需要 64 位指针来容纳具有多达 16 TB 虚拟内存的系统,但大多数数据仍然适合 32 位整数。 对于大多数应用程序,将默认整数大小更改为 64 位只会浪费空间。

在 32 位 Windows 平台上,操作系统会自动修复内核模式内存对齐错误,并使它们对应用程序不可见。 它对调用进程和任何后代进程执行此操作。 此功能通常会大幅降低性能,但尚未在 64 位 Windows 中实现。 因此,如果 32 位驱动程序包含不对齐 bug,则需要在移植到 64 位 Windows 时修复它们。

在 64 位 Windows 中执行 DMA

向驱动程序添加 64 位寻址支持可以显著提高整体系统性能。 对于 DMA这样执行直接内存访问的设备驱动程序来说,这一点尤其重要。 在 64 位 Microsoft Windows 中,执行 DMA 但不支持 64 位寻址的设备驱动程序是双缓冲的,这会导致相对性能降低。

尽管双缓冲通常 在8 GB 系统上),造成的单个百分点的影响相对较小,但这足以影响 I/O 密集型任务,例如数据库活动。 随着物理内存量的增加,这种负面的性能影响也会增加。

若要支持 64 位 DMA,驱动程序应遵循以下准则:

  • 使用 PHYSICAL_ADDRESS 结构进行物理地址计算;
  • 将整个 64 位地址视为有效的物理地址。 例如,驱动程序不应在锁定的缓冲区上调用 MmGetPhysicalAddress 、放弃高 32 位,并将截断的地址传递给 32 位组件适配器。 这会导致内存损坏、I/O 丢失和系统故障;
  • 使用在 Windows 2000 中添加 (GetScatterGatherList 和 PutScatterGatherList) 的高性能Scatter/Gather例程;
  • 检查 Mm64BitPhysicalAddress 全局系统变量的值。 如果为 TRUE,则系统支持 64 位物理寻址;
  • 将 DEVICE_DESCRIPTION 结构的 Dma64BitAddresses 成员设置为 TRUE 以指示驱动程序支持 64 位 DMA 地址;

32 位 Windows 中的 DMA 例程是 64 位就绪的。 如果设备驱动程序正确使用这些例程,则 DMA 代码应在 64 位 Windows 上无需修改即可正常工作。

在 64 位驱动程序中支持 32 位 I/O

Windows 上的 Windows (WOW64) 使 Microsoft Win32 用户模式应用程序能够在 64 位 Windows 上运行。 在转换到 64 位内核之前,它通过截获 Win32 函数调用并将参数从 32 位指针类型转换为 64 位指针类型来执行此操作。 对于所有 Win32 函数,此转换称为 thunking,它会自动完成,但有一个重要例外:传递给 DeviceIoControl 的数据缓冲区。 这些缓冲区的内容(由 InputBuffer 和 OutputBuffer 参数指向)不会被处理,因为它们的结构特定于驱动程序。

注意 尽管缓冲区内容未受到限制,但缓冲区指针将转换为 64 位指针。

用户模式应用程序调用 DeviceIoControl 以将 I/O 请求直接发送到指定的内核模式驱动程序。 此请求包含 I/O 控制代码 (IOCTL) 或文件系统控制代码 (FSCTL) 和指向输入和输出数据缓冲区的指针。 这些数据缓冲区的格式特定于 IOCTL 或 FSCTL,后者又由内核模式驱动程序定义。 由于缓冲区格式是任意的,并且驱动程序知道它,而不是 WOW64,因此将数据转换的任务留给驱动程序。

如果满足以下所有条件,则 64 位驱动程序必须支持 32 位 I/O:

  • 驱动程序向用户模式应用程序公开 IOCTL (或 FSCTL) ;
  • IOCTL 使用的至少一个 I/O 缓冲区包含指针精度数据类型;
  • 无法轻松重写 IOCTL 代码,以消除指针精度缓冲区数据类型的使用;
驱动程序如何识别 32 位调用方

驱动程序可通过两种方式来确定 IOCTL 或 FSCTL 请求的发起方是 32 位还是 64 位应用程序。 第一种是让应用程序标识自身。 第二种是让驱动程序自行确定应用程序是 32 位还是 64 位。

第一种方法涉及在 IOCTL 或 FSCTL 控制代码中定义“64 位”字段。 此字段包含仅为 64 位调用方设置的单个位。 因此,64 位调用方通过使用设置此位的一组单独的 64 位控制代码来标识自己。 32 位调用方使用一组类似的控制代码,其中未设置此位。

第二种方法允许 32 位和 64 位应用程序继续使用相同的 IOCTL 或 FSCTL 代码。 相反,驱动程序通过调用 IoIs32bitProcess 来确定用户模式进程是 32 位还是 64 位。

第一种方法更有效,因为驱动程序会检查位标志,而不是调用内核模式例程。 但是,第二种方法不需要更改用户模式代码。 应使用哪种技术取决于驱动程序的要求以及向其发送 I/O 请求的应用程序。

x64 驱动程序的限制

在基于 x64 的系统上,内核代码和某些内核数据结构受到保护,防止修改。 任何尝试修改此类代码或数据的驱动程序都将导致系统检查 (CRITICAL_STRUCTURE_CORRUPTION) 。

基于 x64 的系统驱动程序必须避免可能触发此 bug 的操作检查。 具体而言,驱动程序不得:

  • 尝试在运行时修改内核代码;
  • 实现和使用自己的堆栈;
  • 修改硬件调度表,例如中断调度表 (IDT) 或全局描述符表 (GDT) ;
  • 修改未记录的内核数据结构;

即使上述操作不会在基于 x86 或基于 Itanium 的系统上触发 bug 检查,驱动程序也不应在任何平台上执行这些操作。 这些操作在 Microsoft Windows 操作系统的未来版本中可能不起作用。


网站公告

今日签到

点亮在社区的每一天
去签到