精华内容
下载资源
问答
  • windows的内存管理技术
    千次阅读
    2020-10-17 10:17:53

    1.虚拟内存: 最适合用来管理大型对象或者结构数组

    2.内存映射文件: 最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行多个进程之间共享数据

    3.内存堆栈: 最适合用来管理大量的小对象

    更多相关内容
  • windows内存管理

    2012-04-18 15:30:13
    windows的内存管理,系统介绍了dos,386下的windows内存管理机制。区分线性地址,物理地址区别……需要同学可以看看
  • 实验五 Windows XP 虚拟内存管理 一 实验目的 1) 了解存储器管理以及虚拟...三 背景知识 虚拟存储器技术是当代计算机中广泛采用的内存管理方案在 Windows XP 中合理的进行虚拟内存的设 置可以更有效的提高系统的工作效
  • Windows内存管理的几种方式和优缺点

    千次阅读 2016-07-27 11:34:03
    Windows内存管理方式主要分为:页式管理、段式管理和段页式管理。 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页。把内存空间按页的大小划分为片或者页面,然后把页式虚拟地址与内存地址建立...

    Windows内存管理方式主要分为:页式管理、段式管理和段页式管理。

    页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页。把内存空间按页的大小划分为片或者页面,然后把页式虚拟地址与内存地址建立一一对应的页表,并用相应的硬件地址转换机构来解决离散地址变换问题。页式管理采用请求调页和预调页技术来实现内外存存储器的统一管理。

    优点:没有外碎片,每个内碎片不超过页的大小。

    缺点:程序全部装入内存,要求有相应的硬件支持,如地址变换机构缺页中断的产生和选择淘汰页面等都要求有相应的硬件支持。增加了机器成本和系统开销。

    段式管理的基本思想是把程序按内容或过程函数关系分成段,每段有自己的名字。一个用户作业或者进程所包含的段对应一个二维线性虚拟空间,也就是一个二维虚拟存储器。段式管理程序以段为单位分配内存,然后通过地址映射机构把段式虚拟地址转换为实际内存物理地址。

    优点:可以分别编写和编译,可以针对不同类型的段采取不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。

    缺点:会产生碎片。

    段页式管理,系统必须为每个作业或者进程建立一张段表以管理内存分配与释放、缺段处理等。另外由于一个段又被划分为若干个页,每个段必须建立一张页表以把段中的虚页变换为内存中的实际页面。显然与页式管理时相同,页表也要有相应的实现缺页中断处理和页面保护等功能的表项。

    段页式管理是段式管理和页式管理相结合而成,具有两者的优点。

    由于管理软件的增加,复杂性和开销也增加。另外需要的硬件以及占用的内存也有所增加,使得执行速度下降。

    展开全文
  • 作为开发Windows驱动程序的程序员,需要比普通程序员更多了解Windows内部的内存管理机制,并在驱动程序中有效地使用内存。在驱动程序的编写中,分配和管理内存不能使用熟知的Win32 API函数,取而代之的是DDK提供的...
     

     作为开发Windows驱动程序的程序员,需要比普通程序员更多了解Windows内部的内存管理机制,并在驱动程序中有效地使用内存。在驱动程序的编写中,分配和管理内存不能使用熟知的Win32 API函数,取而代之的是DDK提供的高效内核函数。C语言和C++中大多数关于内存操作的运行时函数,大多在内核模式下是无法使用的。例如,C语言中的malloc函数和C++中的new操作符等。

     

    内存管理的概念:

     

    1.物理内存:

    PC上有三条总线,分别是数据总线,地址总线和控制总线。32位的CPU寻址能力是4GB。用户最多可以使用4GB的真实物理内存。PC中会拥有许多设备,其中很多设备都拥有自己的设备内存,这部分的设备内存会映射到PC机的物理内存上,读写这段物理地址其实会读写设备内存地址。

     

    2虚拟内存:

    .虽然可以寻址4GB的内存,而PC里往往没有如此多的真实物理内存。操作系统和硬件为使用者提供了虚拟内存的概念。虚拟内存和物理内存之间的转换暂不讨论。

     

    3.用户模式地址和内核模式地址:

    虚拟地址在0~0X7FFFFFFF范围内的虚拟内存,即低2GB的虚拟地址,被称为用户模式地址。而0X80000000~0XFFFFFFFF范围内的虚拟内存,即高2GB的虚拟内存,被称为内核模式地址。

     

    4.Windows驱动程序和进程的关系:

    驱动程序可以看成一个特殊的DLL文件被应用程序加载到虚拟内存中,只不过加载地址是内核模式地址,而不是用户模式地址。

     

    5.分页和非分页内存:

    Windows规定有些虚拟内存页面是可以交换到文件中的,这类内存成为分页内存。而有些虚拟内存页永远不会交换到文件中,这些内存被称为非分页内存。

    当程序的中断请求级在DISPATCH_LEVEL之上时,程序只能使用非分页内存,否则会导致蓝屏死机。

    在编译DDK提供的例程时,可以指定某个例程和某个全局变量是载入分页内存还是非分页内存,需要做如下定义:

    #define PAGEDCODE code_seg("PAGE")
    #define LOCKEDCODE code_seg()
    #define INITCODE code_seg("INIT")
    
    #define PAGEDDATA data_seg("PAGE")
    #define LOCKEDDATA data_seg()
    #define INITDATA data_seg("INIT")


    如果将某个函数在入到分页内存,我们需要在函数的实现中加入如下代码:

    #pragma INITCODE
    VOID SomeFunction()
    {
          PAGED_CODE();
          //do something
    }

    如果要让程序加载到非分页内存,需要在函数的实现中加入如下代码:

    #pragma LOCKEDCODE
    VOID SomeFunction()
    {
          //do something
    }

    还有一种特殊情况,就是某个例程初始化的时候载入内存,然后就可以从内存中卸载掉,例如DriverEntry

    #pragma INITCODE
    extern "C" NTSTATUS DriverEntry(
                               IN PDRIVER_OBJECT pDriverObject,
    			IN PUNICODE_STRING pRegistryPath
    						     )
    {
    
    }


     

    6.内存的分配:

    Windows驱动程序使用的内存资源非常珍贵,局部变量是在栈(stack)空间中,但是驱动程序的栈空间不会像应用程序那么大,所以不适合进行递归调用或者局部变量是大型的结构体,否则请在堆(Heap)中申请。

    堆中申请内存的函数有以下几个:

    PVOID 
    ExAllocatePool(
    		IN POOL_TYPE PoolType, 
    	    IN SIZE_T NumberOfBytes
    		);
    
    PVOID 
    ExAllocatePoolWithTag(
    		IN POOL_TYPE PoolType, 
    		IN SIZE_T NumberOfBytes, 
    		IN ULONG Tag 
    		);
    
    PVOID 
    ExAllocatePoolWithQuota( 
    		IN POOL_TYPE PoolType,
    		IN SIZE_T NumberOfBytes 
    		);
    
    PVOID 
    ExAllocatePoolWithQuotaTag(
    		IN POOL_TYPE PoolType,
    		IN SIZE_T NumberOfBytes,
    		IN ULONG Tag 
    		);


    其中有些重要的参数:

    PoolType: 是个枚举变量,如果此值为NonPagedPool,则分配非分页内存。如果次值为PagedPool,则分配内存分页内存。

    NumberOfBytes:是分配内存的大小,注意最好是4的倍数。

    以上四个函数功能类似,函数以WithQuota结尾的代表分配的时候按额分配。以WithTag结尾的函数和ExAllocatePool功能类似,唯一不同的是多了一个Tag参数,

    系统要求的内存外又额外地多分配4个字节的标签。在调试的时候,可以找出是否有标有这个标签的内存没有被释放。

     

    将分配的内存,进行回收的函数原型如下:

    VOID
    ExFreePool( 
    	IN PVOID P
    		 );
    
    NTKERNELAPI
    VOID
    ExFreePoolWithTag(
    	IN PVOID P,
    	IN ULONG Tag
      );


    参数P就是要释放的内存。

     

    在内存中使用链表:

    链表中可以记录整形,浮点型,字符型或者程序员自定义的数据结构。对于单链表,元素中有一个Next指针指向下一个元素。对于双链表,每个元素有两个指针:BLINK指向前一个元素,FLINK指向下一个元素。

     

    1.链表的结构:

    DDK提供了标准的双向链表。双向链表可以将链表形成一个环。以下是DDK提供的双向链表的数据结构:

    typedef struct _LIST_ENTRY {
       struct _LIST_ENTRY *Flink;
       struct _LIST_ENTRY *Blink;
    } LIST_ENTRY, *PLIST_ENTRY


    这个结构体只有指针没有数据。

     

    2.链表的初始化:

    每个双向链表都是以链表头作为链表的第一个元素。初次使用链表头需要进行初始化,主要将链表头的Flink和Blink两个指针都指向自己。初始化链表头用InitiallizeListHead宏实现。判断链表头是否为空,DDK提供了一个宏简化这种检查,这就是IsListEmpty。

    IsListEmpty(&head);


    程序员需要自己定义链表中每个元素的数据类型,并将LIST_ENTRY结构作为自定义结构的一个子域。例如:

    typedef struct _MYDATASTRUCT
    {
    	LIST_ENTRY ListEntry;
    	ULONG x;
    	ULONG y;
    }MYDATASTRUCT,*PMYDATASTRCUT;


    3.插入链表:

    对链表的插入有两种方式,一种是在链表的头部插入,一种是在链表的尾部插入。

    在头部插入链表使用语句InsertHeadList,用法如下:

    InsertHeadList(&head,&mydata->ListEntry);


    在尾部插入链表使用语句InsertTailList,用法如下:

    InsertTailList(&head,&mydata->ListEntry);

     

    4.链表的删除:

    和插入链表一样,删除链表也有两种方法。一种从头部删除,一种从尾部删除。分别对应RemoveHeadList和RemoveTailList函数。其使用方法如下:

    PLIST_ENTRY = RemoveHeadList(&head);
    
    PLIST_ENTRY = RemoveTailList(&head);


     

    下面代码完整演示向链表进行插入,删除等操作,其主要代码如下:

    typedef struct _MYDATASTRUCT
    {
    	ULONG number;
    	LIST_ENTRY ListEntry;
    }MYDATASTRUCT,*PMYDATASTRCUT;
    
    #pragma INITCODE
    VOID LinkListTest()
    {
    	LIST_ENTRY linkListHead;
    	//初始化链表
    	InitializeListHead(&linkListHead);
        
    	PMYDATASTRCUT pData;
    	ULONG i = 0;
    	//在链表中插入10个元素
    
        KdPrint(("Begain insert to link list"));
    	for (i = 0 ; i < 10 ; i++)
    	{
    		pData = (PMYDATASTRCUT)
    			     ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
    		pData->number = i;
    		InsertHeadList(&linkListHead,&pData->ListEntry);
    	}
    	//从链表中取出,并显示
        KdPrint(("Begain remove from link list\n"));
    	while (!IsListEmpty(&linkListHead))
    	{
    		PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
    		pData = CONTAINING_RECORD(pEntry,
    			              MYDATASTRUCT,
    						  ListEntry);
    		KdPrint(("%d\n",pData->number));
    		ExFreePool(pData);
    	}
    }


    在DebugView中打印log信息:

     

    Lookaside结构:

    频繁申请和回收内存,会导致在内存上产生大量的内存“空洞”,从而导致最终无法申请内存。DDK为程序员提供了Lookaside结构来解决这个问题。

     

    1.使用Lookaside:

    Lookaside一般会在一下情况使用:

    (1)程序员每次申请固定大小的内存

    (2)申请和回收的操作十分频繁

    使用Lookaside对象,首先要初始化Lookaside对象,有以下两个函数可以使用:

    VOID
    ExInitializeNPagedLookasideList(
    			IN PNPAGED_LOOKASIDE_LIST Lookaside, 
    			IN PALLOCATE_FUNCTION Allocate, 
    			IN PFREE_FUNCTION Free,
    			IN ULONG Flags, 
    			IN SIZE_T Size,
    			IN ULONG Tag,
    			IN USHORT Depth 
    			);
    
    VOID
    ExInitializePagedLookasideList( 
    			IN PPAGED_LOOKASIDE_LIST Lookaside,
    			IN PALLOCATE_FUNCTION Allocate, 
    			IN PFREE_FUNCTION Free, 
    			IN ULONG Flags,
    			IN SIZE_T Size,
    			IN ULONG Tag,
    			IN USHORT Depth
    			);


    这两个函数分别对非分页和分页Lookaside对象进行初始化。

    在初始化完Lookaside对象后,可以进行申请内存的操作了,有以下两个函数:

    PVOID
    ExAllocateFromNPagedLookasideList(
    			IN PNPAGED_LOOKASIDE_LIST Lookaside 
    			      );
    
    PVOID
    ExAllocateFromPagedLookasideList(
    			IN PPAGED_LOOKASIDE_LIST Lookaside
    			     );


    这两个函数分别是对非分页和分页内存的申请。

    对Lookaside对象进行回收内存的操作,有以下两个函数:

    VOID
    ExFreeToNPagedLookasideList( 
    				IN PNPAGED_LOOKASIDE_LIST Lookaside,
    				IN PVOID Entry
    				);
    
    VOID
    ExFreeToPagedLookasideList( 
    				IN PPAGED_LOOKASIDE_LIST Lookaside,
    				IN PVOID Entry 
    				);

    这两个函数分别是对非分页和分页内存的回收。

    在使用完Lookaside对象后,需要删除Lookaside对象,有以下两个函数:

    VOID
    ExDeleteNPagedLookasideList(
    				IN PNPAGED_LOOKASIDE_LIST Lookaside
    				);
    
    VOID
    ExDeletePagedLookasideList( 
    				IN PPAGED_LOOKASIDE_LIST Lookaside
    				);


    下面代码完整展示Lookaside对象的使用:

    #pragma INITCODE
    VOID LookasideTest()
    {
    	//初始化Lookaside对象
    	PAGED_LOOKASIDE_LIST pageList;
    	ExInitializePagedLookasideList(&pageList,
    		                         NULL,NULL,0,
    								 sizeof(MYDATASTRUCT),
    								 '1234',
    								 0);
    #define ARRAY_NUMBER 50
    	PMYDATASTRUCT MyObjectArray[ARRAY_NUMBER];
    	//模拟频繁申请内存
    	for (int i = 0 ; i < ARRAY_NUMBER ; i++)
    	{
    		MyObjectArray[i] = 
    			(PMYDATASTRUCT)ExAllocateFromPagedLookasideList(&pageList);
    	}
    	//模拟频繁回收内存
    	for (i = 0 ; i < ARRAY_NUMBER ; i++)
    	{
    		ExFreeToPagedLookasideList(&pageList,MyObjectArray[i]);
    		MyObjectArray[i] = NULL;
    	}
    	//删除Lookaside对象
    	ExDeletePagedLookasideList(&pageList);
    }


    运行时函数:

     

    1.内存间复制(非重叠)

    在驱动程序开发中,经常用到内存的复制。DDK为程序员提供了以下函数:

    VOID
    RtlCopyMemory( 
    		IN VOID UNALIGNED *Destination, 
    		IN CONST VOID UNALIGNED *Source, 
    		IN SIZE_T Length
    		);

    Destination:表示要复制内存的目的地址

    Source:表示要复制内存的源地址

    Length:表示要复制内存的长度,单位是字节

     

    2.内存间的复制(可重叠)

    函数原型:

    VOID
    RtlMoveMemory( 
    		IN VOID UNALIGNED *Destination,
    		IN CONST VOID UNALIGNED *Source, 
    		IN SIZE_T Length
    		);

    Destination:表示要复制内存的目的地址

    Source:表示要复制内存的源地址

    Length:表示要复制内存的长度,单位是字节

     

    3.内存填充:

    驱动程序开发中,还经常用到对某段内存区域用固定字节填充。DDK为程序员提供了函数RtlFillMemory。它在IA32平台下也是个宏,实际是memset函数。

    VOID
    RtlFillMemory( 
    	   IN VOID UNALIGNED *Destination, 
    	   IN SIZE_T Length,
    	   IN UCHAR Fill
    	   );
    


    Destination:目的地址

    Length:长度

    Fill:需要填充的字节

     

    在驱动开发中,还经常需要对某段内存填零,DDK提供的宏是RtZeroBytes和RtZeroMemory。

    VOID
    RtlZeroMemory(
    	   IN VOID UNALIGNED *Destination, 
    	   in SIZE_T Length
    	   );


    Destination:目的地址

    Length:长度

     

    4.内存比较:

    驱动开发中,还会用到比较两块内存是否一致。该函数是RtlCompareMemory,其申明是:

    ULONG
    RtlEqualMemory(
          CONST VOID *Source1,
          CONST VOID *Source2
          SIZE_T Length
    	   );

    Source1:比较的第一个内存地址

    Source2:比较的第二个内存地址
    Length:比较的长度,单位为字节

     

    将这些运行时函数统一做一个实验,代码如下:

    #define BUFFER_SIZE 1024
    #pragma INITCODE
    VOID RtTest()
    {
    	PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool,BUFFER_SIZE);
    	//用零填充内存
    	RtlZeroMemory(pBuffer,BUFFER_SIZE);
    
    	PUCHAR pBuffer2 = (PUCHAR)ExAllocatePool(PagedPool,BUFFER_SIZE);
    	//用固定字节填充内存
    	RtlFillMemory(pBuffer,BUFFER_SIZE,0xAA);
    
    	//内存拷贝
    	RtlCopyMemory(pBuffer,pBuffer2,BUFFER_SIZE);
    
    	//判断内存是否一致
    	ULONG ulRet = RtlCompareMemory(pBuffer,pBuffer2,BUFFER_SIZE);
    	if (ulRet == BUFFER_SIZE)
    	{
    		KdPrint(("The two blocks are same\n"));
    	}
    }


     

     

     

    (下一篇:Windows内核函数)


     

     

    展开全文
  • WINDOWS核心编程——Windows内存管理

    千次阅读 2017-07-18 20:53:07
    想要了解Windows内存体系结构首先要对系统的内存的分段分页和进程隔离机制要有所了解。系统为了对进程进行隔离,使得每个进程只能访问自己申请的内存而不能访问其他进程的内存资源,对每个进程的内存使用线性地址...

    想要了解Windows内存体系结构首先要对系统的内存的分段分页和进程隔离机制要有所了解。系统为了对进程进行隔离,使得每个进程只能访问自己申请的内存而不能访问其他进程的内存资源,对每个进程的内存使用线性地址编制,在通过内存的分页机制在进程需要访问物理内存时通过进程的页表找到世界的物理内存的地址通过系统读写内存中的数据。在早期总线(20位寻址1M)大于寄存器(16位寻址64k)的情况下为了表示更多的物理内存地址采用了分段技术,现在已经不需要分段技术了(32位的内表示4GB,64位内表示16EB)采用平坦模型。

    32位的系统支持4GB的内存,线性地址的各个区间有不同的作用:


    1.空指针赋值分区:用来给空指针赋值的,这个分区不可操作,操作就报错。

    2.用户模式分区:用户代码在这里跑,堆栈都在这里,用户可以随便用,一般出错都在这里。

    3.64kb禁入分区:不知道干什么用的,估计就是为了区隔内核模式跟用户模式的。

    4.内核模式分区:系统运行的空间,所有进程共用的,用户模式的的代码不能访问这部分代码,若要访问续的通过系统提供的API进入到内核态。

    windows的内存体系结构基于虚拟的线性的地址和分页机制。对于线性地址的分配也是以页为单位进行的,物理地址的管理更是以页为单位。我们可以调用函数从地址空间中预定一块内存,在实际使用的时候再从物理内存中调拨,相当于C语言中的声明与定义,当不再需要内存的时候可以还给系统,先将一块内存标记为可用的(标记线性空间中的地址空闲可用),当积攒够了一定的空闲内存是在取消提交(把物理内存归还给操作系统)。对于物理内存而言,在暂时不用或者内存紧张的情况下可以被交换到磁盘上的页交换文件中,在需要的时候(CPU缺页中断)再从也交换文件中载入到内存中,这样就提高了内存的使用效率。页交换文件的使用当然需要一定的代价,频繁的在磁盘与内存将交换页会导致系统性能下降(硬盘颠簸),一般而言采用增加内存的办法比提升CPU对系统的性能改善更大。对于程序的数据可以采用交换页的技术来扩展内存以提高物理内存的使用效率,对于一些相对于数据的内容多变而且大小不可预计的内存使用方式而言交换页确实能提高效率,但是对于可以预知整块内存大小且需要连续的空间而言如文件镜像,固定大小的数据文件等使用内存映射文件是效率更高的方式。分页内存机制调配内存的过程可以粗略的描述如下:


    系统在对内存访问的安全性方面做的不只是按区段来控制内存的访问,也可以对每一个内存页指定保护属性:


    我们将整个4GB的线性地址空间称为虚拟内存(地址称为逻辑地址),我们所有的内存操作只在逻辑地址上完成,系统会帮我们处理物理地址映射,缺页等所有的情况。系统的内存的状态也主要是通过虚拟内存的状态来表现的,主要通过如下接口获得内存的状态:

    //获取系统信息 64位系统要通过GetNativeSystemInfo
    void WINAPI GetSystemInfo(
        LPSYSTEM_INFO lpSystemInfo  
    );
    typedef struct _SYSTEM_INFO {  
      union {  
        DWORD  dwOemId;  
        struct {  
          WORD wProcessorArchitecture;  //处理器体系结构  
          WORD wReserved;  //保留
        } ;  
      } ;  
      DWORD     dwPageSize;   //分页大小
      LPVOID    lpMinimumApplicationAddress;  //进程最小寻址空间
      LPVOID    lpMaximumApplicationAddress; //进程最大寻址空间  
      DWORD_PTR dwActiveProcessorMask;  //处理器掩码; 0..31 表示不同的处理器
      DWORD     dwNumberOfProcessors;  //CPU数量  
      DWORD     dwProcessorType;  //处理器类型
      DWORD     dwAllocationGranularity;  //虚拟内存空间的粒度
      WORD      wProcessorLevel;  //处理器等级
      WORD      wProcessorRevision;  //处理器版本
    } SYSTEM_INFO;  
    
    //获取当前系统中关系内存使用情况
    BOOL WINAPI GlobalMemoryStatusEx(
        LPMEMORYSTATUSEX lpBuffer  
    );
    typedef struct _MEMORYSTATUSEX {  
      DWORD     dwLength;  // sizeof (MEMORYSTATUSEX)
      DWORD     dwMemoryLoad; //已使用内存数量  
      DWORDLONG ullTotalPhys;  //系统物理内存总量  
      DWORDLONG ullAvailPhys;  //空闲的物理内存  
      DWORDLONG ullTotalPageFile;//页交换文件大小  
      DWORDLONG ullAvailPageFile;//空闲的页交换空间  
      DWORDLONG ullTotalVirtual;  //进程可使用虚拟机地址空间大小  
      DWORDLONG ullAvailVirtual;  //空闲的虚拟地址空间大小  
      DWORDLONG ullAvailExtendedVirtual;  //ullAvailExtendedVirtual保留字段
    } MEMORYSTATUSEX, *LPMEMORYSTATUSEX  
    
    //获取当前进程的内存使用情况
    BOOL WINAPI GetProcessMemoryInfo(
        HANDLE Process, //进程句柄
        PPROCESS_MEMORY_COUNTERS ppsmemCounters, //返回内存使用情况的结构
        DWORD cb  //结构的大小
    ); 
    typedef struct _PROCESS_MEMORY_COUNTERS_EX {  
      DWORD  cb;  //结构的大小
      DWORD  PageFaultCount; //发生的页面错误  
      SIZE_T PeakWorkingSetSize;  //使用过的最大工作集  
      SIZE_T WorkingSetSize;      //目前的工作集  
      SIZE_T QuotaPeakPagedPoolUsage;//使用过的最大分页池大小  
      SIZE_T QuotaPagedPoolUsage;  //分页池大小  
      SIZE_T QuotaPeakNonPagedPoolUsage;//非分页池使用过的  
      SIZE_T QuotaNonPagedPoolUsage;  //非分页池大小  
      SIZE_T PagefileUsage; //页交换文件使用大小  
      SIZE_T PeakPagefileUsage; //历史页交换文件使用  
      SIZE_T PrivateUsage;  //进程运行过程中申请的内存大小  
    } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX  
    
    //查询当前进程虚拟地址空间的某个地址所属的块信息
    SIZE_T WINAPI VirtualQuery(
        LPCVOID                   lpAddress, //查询内存的地址
        PMEMORY_BASIC_INFORMATION lpBuffer, //接收内存信息
        SIZE_T                    dwLength //结构的大小
    );
    //查询进程虚拟地址空间的某个地址所属的块信息
    DWORD VirtualQueryEx(
        HANDLE hProcess, //进程句柄
        LPCVOID lpAddress, //查询内存的地址
        PMEMORY_BASIC_INFORMATION lpBuffer, //接收内存信息
        DWORD dwLength //结构的大小
    );
    typedef struct _MEMORY_BASIC_INFORMATION {  
      PVOID  BaseAddress;  //区域基地址  
      PVOID  AllocationBase;//使用VirtualAlloc分配的基地址  
      DWORD  AllocationProtect; //保护属性  
      SIZE_T RegionSize;    //区域大小  
      DWORD  State;     //页属性  
      DWORD  Protect;  //区域属性  
      DWORD  Type;  //区域类型  
    } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;  
    程序不能直接操作物理内存的,所有的数据都需要保存在线性的虚拟内存(逻辑地址)中。使用虚拟内存主要使用函数VirtualAlloc来预定和提交内存,使用VirtualFree来归还或取消提交内存。

    虚拟内存的操作以页为粒度,适合用来管理大型对象数组或大型结构数组。对于存在页交换文件的内存页若我们能确定整页的内存数据不会改变,或者放弃在内存中的改变,下回直接从页交换文件中重新载入,则称该内存页为可重设的,不需要被交换到页文件中,直接覆盖其中的内容,在需要的时候重新从也文件中载入。预定提交重设用的同一个函数说明如下:

    //预定虚拟内存和调拨物理内存,失败返回NULL,成功返回lpAddress的取整的值
    LPVOID VirtualAlloc{
         LPVOID lpAddress, // 要分配的内存区域的地址,按分配粒度向上取整,为NULL则由系统决定
         DWORD dwSize, // 分配的大小,分配粒度的整数倍
         DWORD flAllocationType, // 分配的类型
         DWORD flProtect // 该内存的初始保护属性
    };
    对函数VirtualAlloc中的类型和保护属性说明如下:


    VirtualAlloc的逆向操作为VirtualFree用于释放和清理虚拟内存:

    BOOL WINAPI VirtualFree(
        LPVOID lpAddress, //释放(取消预定或提交)的页的首地址
        SIZE_T dwSize,  //大小
        DWORD dwFreeType  //MEM_DECOMMIT 取消VirtualAlloc提交的页, MEM_RELEASE 释放指定页
        //当释放整个区域时 dwFreeType 设置为MEM_RELEASE,lpAddress设置为区域的起始地址,dwSize设置为0,
    );
    对于VirtualAlloc时指定的保护方式可以通过函数VirtualProtect来更改:

    BOOL VirtualProtect(
        LPVOID lpAddress, // 目标地址起始位置
        DWORD dwSize, // 大小
        DWORD flNewProtect, // 请求的保护方式
        PDWORD lpflOldProtect // 保存老的保护方式
    );
    为了允许一个32位进程分配和访问更多的物理内存,突破这一受限地址空间所能表达的内存范围,Windows提供了一组函数,称为地址窗口扩展(AWE , Address  Windowing  Extensions)。用到的不多可以稍微了解下。

    而更常见的在有限的地址空间中处理大数据量(大到4GB的地址空间无法容纳所有数据)是,我们通常采用内存映射文件的办法一段段的处理数据。所谓映射就是把一段逻辑地址与文件的一段内容一一对应起来(同一段地址可以多次对应不同的文件内容)。映射原理如下(图片摘自网络如有版权问题请联系删除):

    正是由于内存映射文件的这几个特性所以特别合适用来处理下列事情:

    1:系统使用内存映射文件来将exe或是dll文件本身作为后备存储器,而非系统页交换文件,这大大节省了系统页交换空间,由于不需要将exe或是dll文件加载到页系统交换文件,也提高了启动速度。由于是映射到各自的逻辑地址的所以每个进程保存自己的副本,所有的变量之间也互不共享,但是可以通过DLL的数据段在使用同一DLL的不同进程间共享变量。
    2:使用内存映射文件来将磁盘上的文件映射到进程的空间区域,使得开发人员操作文件就像操作内存数据一样,将对文件的操作交由操作系统来管理,简化了开发人员的工作。这是最常用的方式,使用方式如下:

    1.创建或打开一个文件内核对象
    HANDLE WINAPI CreateFile(
        LPCTSTR lpFileName,
        DWORD dwDesiredAccess,
        DWORD dwShareMode,
        LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        DWORD dwCreationDisposition,
        DWORD dwFlagsAndAttributes,
        HANDLE hTemplateFile
    );
    2.创建一个文件映射内核对象
    HANDLE WINAPI CreateFileMapping(
        HANDLE hFile,  //文件句柄
        LPSECURITY_ATTRIBUTES lpAttributes, //安全属性
        DWORD flProtect, //保护属性
        DWORD dwMaximumSizeHigh, //文件映射的最大长度的高32位
        DWORD dwMaximumSizeLow, //文件映射的最大长度的低32位
        LPCTSTR lpName //内核文件命名
    );
    5.关闭文件对象
    CloseHandle(hFile);
    
    3.将文件映射对象映射到进程地址空间
    LPVOID WINAPI MapViewOfFile(
        HANDLE hFileMappingObject, //文件句柄
        DWORD dwDesiredAccess, //文件数据的访问方式要与CreateFileMapping()的保护属性相匹配
        DWORD dwFileOffsetHigh, //表示文件映射起始偏移的高32位
        DWORD dwFileOffsetLow, //表示文件映射起始偏移的低32位
        SIZE_T dwNumberOfBytesToMap //指定映射文件的字节数
    );
    
    6.关闭文件映射对象
    CloseHandle(hFileMapping);
    
    4.从进程的地址空间中撤消文件数据的映像
    BOOL UnmapViewOfFile(
        PVOID pvBaseAddress //pvBaseAddress由MapViewOfFile函数返回
    );
    
    //可以按以上顺序执行或者看情况执行4,5,6
    //对于修改过的数据的一部分或全部强制重新写入磁盘映像中
    BOOL FlushViewOfFile(
       PVOID pvAddress, //内存映射文件中的视图的一个字节的地址
       SIZE_T dwNumberOfBytesToFlush //想要刷新的字节数
    );
    对于一些参数的说明如下:

    使用fdwProtect 参数设定的部分保护属性

    dwDesiredAccess用于标识如何访问该数据



    3:windows提供了多种进程间通信的方法,但他们都是基于内存映射文件来实现的。

    对于进程间通信只要在不同进程中映射了同一个文件内容,当其中一个映射被改变时(就算还没有保存到磁盘上)其他进程自动会获取到改变。

    windows的进程除了直接向系统申请内存之外还可以使用运行时库提供的内存堆和栈,简单的有如下说明:

    http://blog.csdn.net/pokeyode/article/details/53303029

    http://blog.csdn.net/pokeyode/article/details/53336826
    虽然运行时库提供的堆足以满足我们的需要,但我们还是会基于一下原因来创建自己的堆(引用自):

    一:对数据保护。创建两个或多个独立的堆,每个堆保存不同的结构,对两个堆分别操作,可以使问题局部化。
    二:更有效的内存管理。创建额外的堆,管理同样大小的对象。这样在释放一个空间后可以刚好容纳另一个对象。
    三:内存访问局部化。将需要同时访问的数据放在相邻的区域,可以减少缺页中断的次数。
    四:避免线程同步开销。默认堆的访问是依次进行的。堆函数必须执行额外的代码来保证线程安全性。通过创建额外的堆可以避免同步开销。
    五:快速释放。我们可以直接释放整个堆而不需要手动的释放每个内存块。这不但极其方便,而且还可以更快的运行。
    要创建并管理自己的堆,需要使用以下接口,首先要创建堆:

    HANDLE HeapCreate(
        DWORD fdwOptions, //如何操作堆
        SIZE_T dwInitilialize, //一开始要调拨给堆的字节数向上取整到CPU页面大小的整数倍
        SIZE_T dwMaximumSize //堆所能增长到的最大大小,即预定的地址空间的最大大小。若为0,那么堆可增长到用尽所有的物理存储器为止。
    ); 

    fdwOptions表示对堆的操作该如何进行
    HEAP_NO_SERIALIZE标志使得多个线程可以同时访问一个堆,这使得堆中的数据可能会遭到破坏,因此应该避免使用。
    HEAP_GENERATE_EXCEPTIONS标志告诉系统,每当在堆中分配或者重新分配内存块失败的时候,抛出一个异常。
    HEAP_CREATE_ENABLE_EXECUTE标志告诉系统,我们想在堆中存放可执行代码。如果不设置这个标志,那么当我们试图在来自堆的内存块中执行代码时,系统会抛出EXCEPTION_ACCESS_VIOLATION异常。

    有了堆之后从堆中分配内存时要:

    1.遍历已分配的内存的链表和闲置内存的链表。
    2.找到一块足够大的闲置内存块。
    3.分配一块新的内存,将2找到的内存块标记为已分配。
    4.将新分配的内存块添加到已分配的链表中。

    调用函数来从堆中分配并在需要时调整内存大小:

    PVOID HeapAlloc(
        HANDLE hHeap, //堆句柄,表示要从哪个堆分配内存
        DWORD fdwFlags, //堆分配时的可选参数
        SIZE_T dwBytes //要分配堆的字节数
    );  
    PVOID HeapReAlloc(
        HANDLE hHeap, //堆句柄
        DWORD fdwFlags, //HeapAlloc的fdwFlags一样
        PVOID pvMem, //指定要调整大小的内存块
        SIZE_T dwBytes //指定内存块的新大小
    );  
    fdwFlags说明如下:

    HeapReAlloc的fdwFlags特别的有HEAP_REALLOC_IN_PLACE_ONLY 如果HeapReAlloc函数能在不移动内存块的前提下就能让它增大,那么函数会返回原来的内存块地址。另一方面,如果HeapReAlloc必须移动内存块的地址,那么函数将返回一个指向一块更大内存块的新地址。如果一个内存块是链表或者树的一部分,那么需要指定这个标志。因为链表或者树的其他节点可能包含指向当前节点的指针,把节点移动到堆中其他的地方会破坏链表或树的完整性。

    若成功则返回内存地址若失败则返回NULL,若指定了HEAP_GENERATE_EXCEPTIONS报异常:


    在不需要内存是把内存归还给堆:

    BOOL HeapFree( 
        HANDLE hHeap, //堆句柄
        DWORD fdwFlags,
        PVOID pvMem //指定要调整大小的内存块
    ); 
    
    在不需要堆时销毁堆

    BOOL HeapDestroy(HANDLE hHeap);  

    展开全文
  • windows底层内存管理技术

    千次阅读 2009-10-16 09:27:00
    1.1. 物理地址在物理存储器上的内存地址,一般由内核管理,应用程序无法直接得到。1.2. 虚拟地址在进程私有空间中的地址,即应用程序指针所指向的地址值。1.3. 寻址空间进程所能够...2. windows内存结构2.1. 虚拟地址空
  • Windows 内存机制说明

    千次阅读 2018-11-06 22:41:29
    1.内存分布概述  每个进程都有自己的虚拟地址空间,对于32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到0xFFFFFFFF之间的任意一值。对于64位进程来说,由于64位指针可以表示从0x...
  • Windows服务器查看内存型号

    千次阅读 2021-08-08 09:22:12
    Windows服务器查看内存型号 内容精选换一换以Windows 2008 R2 64bit为例,介绍查询Windows弹性云服务器磁盘分区与磁盘设备对应关系的方法。登录Windows弹性云服务器。单击桌面左下角的“开始”菜单。选择“控制面板 ...
  • windows内存占用过高

    千次阅读 2022-04-07 20:03:46
    Windows开机内存占用超过30%(6G/18G),Come on: Round 1 msconfig->常规:有选择的启动->服务->隐藏所有Microsoft服务,全部禁用。Ban了再说。需要的自己单独启。 发现触摸板配置没了(主要是连接外部...
  • 这篇文章将带着大家来学习《Windows黑客编程技术详解》,其作者是甘迪文老师,推荐大家购买来学习。作者将采用实际编程和图文结合的方式进行分享,并且会进一步补充知识点。第三篇文章主要介绍木马病毒启动技术,...
  • 前面的《Windows内存体系(3) – 内存映射文件》文章,对内存映射文件技术的原理进行了介绍,本篇文章着重介绍该技术的使用场景。 一、内存映射文件技术介绍 常用的有Win32 API的CreateFile()、WriteFile()、...
  • Windows虚拟内存简介

    千次阅读 2017-09-30 15:13:00
    Windows系统中,系统内存本身的空间可能非常有限,但是通过虚拟内存(Virtual Memory),可以让程序...通常在系统中,虚拟内存的大小都要大于系统的实际内存大小,因此内存管理器会将一部分虚拟内存中的内容映射到
  • Windows 任务管理器中的几个内存概念

    千次阅读 2019-01-09 09:36:11
    Windows 任务管理器中的几个内存概念 我们使用的大部分 PC 是基于 Intel 微处理器的 x86 和 x64 架构计算机.... 因此, 我们面对的 windows 避免不了和... 比如接下来要说到的内存管理. 为简单起见, 我们只讨论 x86 ...
  • Android 内存管理机制

    千次阅读 多人点赞 2018-09-03 14:41:45
    内存管理基础:从整个计算机领域简述主要的内存管理技术。 Linux的内存管理机制:Android毕竟是基于Linux内核实现的操作系统,因此有必要了解一下Linux的内存管理机制。 Android的内存管理相关知识:Android又...
  • 这篇文章将带着大家来学习《Windows黑客编程技术详解》,其作者是甘迪文老师,推荐大家购买来学习。作者将采用实际编程和图文结合的方式进行分享,并且会进一步补充相关知识点。第六篇文章主要介绍木马病毒提权技术...
  • 面试题总结之windows/linux内存管理

    千次阅读 2017-08-20 23:09:48
    前言请说说你对windows/linux内存管理的认识解答内存管理的必要性出现在早期的计算机系统当中,程序是直接运行在物理内存中,每一个程序都能直接访问物理地址。如果这个系统只运行一个程序的话,并且这个程序所需的...
  • C / C++ 内存管理

    千次阅读 多人点赞 2022-05-27 09:35:36
    2、C语言中动态内存管理方式 malloc / calloc / realloc / free 3、C++内存管理方式 new / delete 操作内置类型 new / delete 操作自定义类型 4、operator new与operator delete函数(重要点进行讲解) ...
  • 操作系统(内存管理)

    热门讨论 2009-09-20 12:55:25
    文将对 Linux™ 程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言。文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半...
  • win10 内存泄漏,查不到对应进程,最全的解决方案 开发人员 应用人员适用
  • C++内存管理技术内幕

    千次阅读 2015-11-22 09:19:04
    这几天在整理硬盘的资料,发现一个PDF,名字叫《C++内存管理技术内幕》,名字很霸气,于是顺着好奇心打开看看。花了一个多小时,终于看完,看完的感觉就是相见恨晚啊,写的如此之好,想看看这篇文章是谁写的,结果找...
  • 详解Windows内存分页机制

    千次阅读 2015-03-14 13:53:07
    标 题: 【原创】详解Windows内存分页机制 作 者: 莫灰灰 时 间: 2011-06-11,19:18:49 链 接: http://bbs.pediy.com/showthread.php?t=135274 昨天新买了两本书, 看到了内存分页部分, 特此记录下,
  • windows7内存管理机制

    千次阅读 2011-04-13 10:55:00
    虽说不是成为了一个“专家”,但也对从vista开始引进的新的内存管理机制Superfetch有了一定的了解。发现了很多人一开始(包括我)对于vista、7的内存的使用的误区。一般来说,从XP升的用户大多都认为系统空闲时内存...
  • Win10内存管理探索

    千次阅读 2019-12-05 22:30:52
    然后就开启了我的内存管理探索之旅~ 1. 任务管理器—内存解析 查看内存状况的第一反应是,任务管理器~ 我们发现一个奇怪的事情,PyCharm 848MB占16.3%?算下来,我们的内存只有5个G左右?不是8个G嘛? **PS:**...
  • 2 控制内存分配在ESXi上,VMkernel管理所有的计算机内存内存中的一部分由VMkernel消耗用以运行自身代码及存储数据,剩余的部分供虚拟机及其虚拟机监控程序(VMX)使用。如所有物理资源一样,内存是有限资源。管理...
  • 从这篇文章开始,作者将带着大家来学习《Windows黑客编程技术详解》,其作者是甘迪文老师,推荐大家购买来学习。作者将采用实际编程和图文结合的方式进行分享,并且会进一步补充知识点,希望对您有所帮助。第二篇...
  • Windows内存管理方式主要分为:页式管理、段式管理和段页式管理 首先看一下“基本的存储分配方式”种类:   1. 离散分配方式的出现  由于连续分配方式会形成许多内存碎片,虽可通过“紧凑”功能...
  • 深入解析windows系统内核,加深windows平台开发技术的理解。由微软专业人员讲解,很不错的视频资料。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 221,250
精华内容 88,500
关键字:

windows的内存管理技术