47、Windows驱动程序模型笔记(五),内存管理

内存管理

1)内核模式与用户模式地址

wps_clip_image-4593

图示 地址空间中用户模式部分和内核模式部分

    每个用户模式进程都有自己的地址上下文,它把用户模式的虚拟地址映射成一组唯一的物理页帧。这意味着,当Windows NT调度器把控制从一个进程的当前线程切换到另一个进程的某个线程时,与进程相对应的虚拟地址空间也被更换。线程切换的一个步骤就是改变处理器当前使用的页表,以便它能引用新线程的进程上下文。

    在编写驱动程序时我们要遵守下面原则:

    决不(或几乎从不)直接引用用户模式的内存地址,无论何时我们需要访问计算机内存,都要使用内核模式的虚拟地址。

2)页大小

    在虚拟内存系统中,操作系统以固定大小的页帧组织物理内存和交换文件。在WDM驱动程序中,常量PAGE_SIZE指出页的大小。在某些Windows NT计算机中,一页有4096字节;在另一些计算机中,一页有8192字节。有一个相关常量PAGE_SHIFT,你可以从下面语句中看出它的值:

PAGE_SIZE == 1 << PAGE_SHIFT

下面预处理宏可以简化页大小的使用:

•ROUND_TO_PAGES 把指定值舍入为下一个页边界。例如,在4KB页的计算机上,ROUND_TO_PAGES(1)的结果为4096,ROUND_TO_PAGES(4097)的结果为8192。

•BYTES_TO_PAGES 得出给定的字节量需要多少页来保存。例如,BYTES_TO_PAGES(42)在所有平台上都等于1,而BYTES_TO_PAGES(5000)在4KB页的平台上为2,在8KB页的平台上为1。

•BYTE_OFFSET 返回虚拟地址的字节偏移部分。例如,在4KB页的计算机上,BYTE_OFFSET(0x12345678)的结果为0x678。

•PAGE_ALIGN 把虚拟地址舍向上一个页边界。例如,在4KB页的计算机上,PAGE_ALIGN(0x12345678)的结果为0x12345000。

•ADDRESS_AND_SIZE_TO_SPAN_PAGES 返回从指定虚拟地址开始的指定字节数所跨过的页数。例如,在4KB的计算机上, ADDRESS_AND_SIZE_TO_SPAN_PAGES(0x12345FFF,2)的结果为2,因为这两个字节跨过了页边界。

3)Windows NT把内核模式地址空间分成分页内存池和非分页内存池。(用户模式地址空间总是分页的) 必须驻留的代码和数据放在非分页池;不必常驻的代码和数据放在分页池中。

执行在高于或等于DISPATCH_LEVEL级的代码不可以引发页故障。

#ifdef ALLOC_DATA_PRAGMA

#pragma data_seg("PAGE")

#endif

data_seg编译指示使所有在其后声明的静态数据变量进入分页池。这个编译指示与alloc_text完全不同。一个分页段可以从#pragma data_seg("PAGE")出现的地方开始到#pragma data_seg()出现的地方结束。而Alloc_text仅应用于单个函数。

    为了检测编译器是否是Microsoft的编译器,可以测试预定义宏_MSC_VER是否存在。

掉电期间是释放锁定内存页的最佳时期。

服务函数

描述

MmLockPagableCodeSection

锁定含有给定地址的代码段

MmLockPagableDataSection

锁定含有给定地址的数据段

MmLockPagableSectionByHandle

用MmLockPagableCodeSection返回的句柄锁定代码段(仅用于Windows 2000)

MmPageEntireDriver

解锁所有属于某驱动程序的页

MmResetDriverPaging

恢复整个驱动程序的编译时分页属性

MmUnlockPagableImageSection

为一个锁定代码段或数据段解锁

服务函数或宏

描述

PushEntryList

向链表顶加入元素

PopEntryList

删除最上面的元素

4)把C/C++编程规范引入驱动编程中

如RemoveHeadList是一个宏,如果if语句中不用{},则出问题。

5)图示显示了 lookaside链表的概念。假设有一个可以在水池中直上直下平衡的玻璃杯子。这个杯子就代表lookaside链表对象。当初始化该对象时,你告诉系 统需要多大的内存块(杯中的水滴)。在早期版本的Windows NT中,你还要指出杯子的容量,但现在的操作系统可以自动适应。为了满足一个内存分配请求,系统首先尝试着从链表中取出(删除)一块内存(从杯子中取出一 滴水)。如果连一块内存也没有,系统就从外面内存池中取。相反,释放内存时,系统首先尝试着放到链表上(向杯子中加入一滴水)。但是,如果链表满了,那么 这个内存块就返回到外界的内存池中(杯子中的水溢出到水池)。

wps_clip_image-21083

图示. Lookaside链表

4、字符串

String Manipulation

http://msdn.microsoft.com/en-us/library/ee479680.aspx

Buffer Manipulation

http://msdn.microsoft.com/en-us/library/ee479504.aspx

P89串处理函数

操作

ANSI串函数

Unicode串函数

Length

strlen

wcslen

Concatenate

strcat, strncat

wcscat, wcsncat,

RtlAppendUnicodeStringToString,

RtlAppendUnicodeToString

Copy

strcpy, strncpy,

RtlCopyString

wcscpy, wcsncpy,

RtlCopyUnicodeString

Reverse

_strrev

_wcsrev

Compare

strcmp, strncmp,

_stricmp, _strnicmp,

RtlCompareString,

RtlEqualString

wcscmp, wcsncmp, _wcsicmp,

_wcsnicmp,

RtlCompareUnicodeString,

RtlEqualUnicodeString,

RtlPrefixUnicodeString

Initialize

_strset, _strnset,

RtlInitAnsiString,

RtlInitString

_wcsnset, RtlInitUnicodeString

Search

strchr, strrchr, strspn,

strstr

wcschr, wcsrchr, wcsspn, wcsstr

Upper/lowercase

_strlwr, _strupr,

RtlUpperString

_wcslwr, _wcsupr,

RtlUpcaseUnicodeString

Character

isdigit, islower, isprint,

isspace, isupper, isxdigit,

tolower, toupper,

RtlUpperChar

towlower, towupper,

RtlUpcaseUnicodeChar

Format

sprintf, vsprintf, _snprintf,

_vsnprintf

swprintf, _snwprintf

String conversion

atoi, atol, _itoa

_itow, RtlIntegerToUnicodeString,

RtlUnicodeStringToInteger

Type conversion

RtlAnsiStringToUnicodeS

ize, RtlAnsiStringToUnicodeS

tring

RtlUnicodeStringToAnsiString

Memory release

RtlFreeAnsiString

RtlFreeUnicodeString

1)处理blob数据的服务函数

. 处理blob数据的服务函数

服务函数或宏

描述

memchr

blob中寻找一个字节

memcpy, RtlCopyBytes, RtlCopyMemory

复制字节,不允许重叠

memmove, RtlMoveMemory

复制字节,允许重叠

memset, RtlFillBytes, RtlFillMemory

用给定的值填充blob

memcmp, RtlCompareMemory, RtlEqualMemory

比较两个blob

memset, RtlZeroBytes, RtlZeroMemory

blob清零

•内存的“copy”和“move”操作之间的区别在于可否容忍源和目的相重叠。move操作不管源和目的是否重叠。而copy操作在源和目的有任何重叠时不工作。

•“byte” 操作和“memory”操作的区别是操作的间隔尺寸。byte操作保证按字节为单位执行。而memory操作可以在内部使用更大的块,所有这些块的和等于 指定的字节数。这个区别会根据平台的不同而改变,在32位Intel计算机上,byte操作实际上是对应memory操作的宏。但在Alpha平台 上,RtlCopyBytes与RtlCopyMemory是完全不同的函数。

5、注册表

服务函数

描述

IoOpenDeviceRegistryKey

打开PDO专用键

IoOpenDeviceInterfaceRegistryKey

打开与注册设备接口相连的键

RtlDeleteRegistryValue

删除一个注册表值

RtlQueryRegistryValues

从注册表中读取多个值

RtlWriteRegistryValue

向注册表写一个值

ZwClose

关闭注册表键句柄

ZwCreateKey

创建一个注册表键

ZwDeleteKey

删除一个注册表键

ZwEnumerateKey

枚举子键

ZwEnumerateValueKey

枚举某注册表键中的值

ZwFlushKey

把注册表更改提交到磁盘

ZwOpenKey

打开一个注册表键

ZwQueryKey

取关于某注册表键的信息

ZwQueryValueKey

取某个注册表键中的值

ZwSetValueKey

置某个注册表键中的值

表示. 注册表访问函数

6、浮点数

    在Intel处理器上,浮点协处理器还可以执行MMX指令。在历史上,驱动程序在执行浮点运算上有两个问题。对于没有浮点协处理器的计算机,操作系统将用 软件仿真一个,但是仿真的浮点协处理器会消耗很大的CPU处理能力,并且需要一个处理器异常来捕捉浮点指令。在内核模式中处理异常,尤其是在提升的 IRQL级上,是困难的。另外,在有浮点协处理器的计算机上,由于CPU结构上的原因,当线程上下文切换时,需要一个耗时的操作来保存和恢复浮点协处理器 的状态。所以,通常的做法是禁止在内核模式驱动程序中使用浮点运算。

    Microsoft建议,除非必要,应避免在内核模式驱动程序中使用浮点运算。

关于如何使用及更多信息,可以参见《Windows驱动程序模型设计》,P99

原文地址:https://www.cnblogs.com/mydomain/p/1925441.html