25.windows内存管理

1.windows内存地址空间

  (1)地址空间

     程序中可以寻址的最大范围,对于32操作系统,地址空间的范围为0-4G(2^32),地址空间越大,相对程序的编写就会容易。

  (2)地址空间的划分

     a.用户地址空间:0-2G(0-7FFFFFFF)

       存放用户的程序和数据。用户空间的代码是不能访问内核空间的数据和代码。

       空指针区(NULL区,0-64K)- 系统将地址小于64K的指针都认为是空指针。

       用户区

       64K禁入区(0x7FFEFFFF - 0x7FFFFFFF)

     b.内核地址空间2G-4G

       存放内核的代码和数据,例如系统驱动。内核空间代码是可以访问用户空间的。

2.windows内存

  (1)区域

     区域就是连续的一块内存。区域的大小一般是64K或64K的整数倍。每个区域都有自己的状态:

     空闲:没有被使用

     私有:被预定的区域

     映像:存放代码

     映射:存放数据

  (2)物理内存(简单说就是插在主板上的内存条)

     系统可以使用的实际内存。

     CPU可以直接访问的只有内存,访问其他外围设备都是以内存做中转。

  (3)硬盘交换文件(虚拟内存)

     将硬盘文件虚拟成内存使用。(pagefile.sys文件)

     CPU如果要访问虚拟内存数据,必须将虚拟内存数据放到物理内存。

     那么我们申请的一段内存是物理内存还是硬盘交换文件呢?

     以我们内存中数据的使用频率为标准,如果频率高,系统就会把数据放到物理内存;否则,就放到硬盘交换文件中。

  (4)内存页

     系统管理内存的最小单位。内存页大小为4k,每个内存页都有自己的权限。

  (5)页目标

     指针地址

     31          22  21          12  11             0

              |------------|-------------|------------|         

            10位           10位             12位

     2^10 = 1024      1024            4k

                  页目(层号)    页表(页号)  页内偏移

  (6)从内存获取数据的过程

     a.根据地址在物理内存中查找相应的位置,如果找到物理内存,取回数据;如果未找到,执行b

     b.根据地址去虚拟内存中查找相应的位置,如果找到,执行c;如果未找到,那么地址没有内存空间,返回错误

     c.将该地址所在的内存页置换到物理内存中,同时将原物理内存数据存到虚拟内存中。

     d.将物理内存中的数据返回给使用者。

  (7)内存分配

     虚拟内存分配:适合大内存分配,一般是1M以上的内存。

     堆内存分配:适合小内存分配,一般是1M以下的内存。(malloc/new)

     栈内存分配:适合小内存分配,一般是1M以下的内存。

 3.虚拟内存分配

  (1)虚拟内存分配

     速度快,大内存效率高。将内存和地址分配分别执行,可以在需要的时候再提交内存。常用于大型电子表格等处理。

  (2)虚拟内存使用

     a.申请地址/提交内存

      LPVOID  VirtualAlloc( LPVOID   lpAddress,          //提交地址或NULL

                                               SIZE_T   dwSize,              //分配的大小

                                               DWORD  flAllocationType,  //分配方式

                                               DWORD  flProtect);           //内存访问方式

      分配成功,返回地址

      分配方式:MEM_COMMIT - 提交内存,分配之后返回地址和内存空间

                               MEM_RESERVE - 保留地址,分配之后只返回地址,内存空间不生成。要使用内存必须再次提交。

     b.使用

     c.释放

      BOOL VirtualFree( LPVOID   lpAddress,      //释放地址

                                          SIZE_T   dwSize,          //释放的大小

                                          DWORD  dwFreeType);  //释放的方式

      释放方式:MEM_DECOMMIT - 只释放内存

          MEM_RELEASE - 地址和内存都释放

     补充:VOID  GlobalMemoryStatus( LPMEMORYSTATUS  lpBuffer );  //获得当前计算机物理和虚拟内存使用情况

  相关代码:

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"


void Status()
{
    MEMORYSTATUS ms = { 0 };
    ms.dwLength = sizeof(ms);
    GlobalMemoryStatus(&ms);
    printf("Load:%d
", ms.dwMemoryLoad);
    printf("TotalPhys:%u
", ms.dwTotalPhys);
    printf("AvliaPhys:%u
", ms.dwAvailPhys);
    printf("TotalVirt:%u
", ms.dwTotalPageFile);
    printf("AvliaVirt:%u
", ms.dwAvailPageFile);
    printf("TotalAddr:%u
", ms.dwTotalVirtual);
    printf("AvliaAddr:%u
", ms.dwAvailVirtual);
    printf("*******************
");
}
void VirtualInfo()
{
    /*
    MEM_COMMIT  - 地址和内存都要
    MEM_RESERVE - 只申请地址,不要内存
    MEM_DECOMMINT - 只释放内存,不释放地址
    MEM_RELEASE   - 地址和内存都释放
    */
    Status();
    //申请1G个地址和1G内存
    char *pszText = (char*)VirtualAlloc(NULL, 1024*1024*1024, MEM_COMMIT, PAGE_READWRITE);
    Status();
    VirtualFree(pszText, 0, MEM_RELEASE);
    Status();
}
void VirtualCommit()
{
    char *pszText = (char*)VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT, PAGE_READWRITE);
    strcpy_s(pszText, 13, "hello commit");
    printf("%s
", pszText);
    VirtualFree(pszText, 0, MEM_RELEASE);
}
void VirtualReserve()
{
    //分配1G地址
    char *pszText = (char*)VirtualAlloc(NULL, 1024 * 1024 * 1024, MEM_RESERVE, PAGE_READWRITE);    
    //提交内存,1个字节,但是提交是按内存页(4096字节)的整数倍倍提交
    //char *pszText1 = (char*)VirtualAlloc(pszText, 1, MEM_COMMIT, PAGE_READWRITE);    
    char *pszText1 = (char*)VirtualAlloc(pszText + 4096, 1, MEM_COMMIT, PAGE_READWRITE);
    strcpy_s(pszText1 + 4082, 14, "hello reserve");
    printf("%s
", pszText1 + 4082);    
    VirtualFree(pszText, 0, MEM_RELEASE);
}
int _tmain(int argc, _TCHAR* argv[])
{
    //VirtualInfo();
    //VirtualCommit();
    VirtualReserve();
    return 0;
}
View Code

  运行结果:

  

4.堆内存Heap

  (1)堆内存分配

     适合小内存分配,一般小于1M的内存。

     一般每个程序都有自己的堆,默认大小为1M,会根据使用情况进行调整。

  (2)堆的使用

     a.堆信息

      GetProcessHeap - 获得程序的第一个堆

      GetProcessHeaps - 获取程序中所有的堆

     b.创建堆

      HANDLE  HeapCreate( DWORD  flOptions,             //创建选项

                                                 SIZE_T   dwInitialSize,         //初始化大小

                                                 SIZE_T   dwMaximumSize);  //最大值

      成功返回堆句柄

      创建选项:HEAP_GENERATE_EXCEPTIONS - 创建失败,抛出异常

           HEAP_NO_SERIALIZE - 支持不连续存取

     c.获取堆所维护内存的某部分地址(从堆中分配内存)

      LPVOID  HeapAlloc( HANDLE  hHeap,      //堆句柄

                                             DWORD  dwFlags,    //分配方式

                                             SIZE_T   dwBytes);  //分配大小

      成功返回地址

     d.使用内存

     e.释放内存

      BOOL  HeapFree( HANDLE hHeap,    //堆句柄

                                          DWORD dwFlags,  //释放方式

                                          LPVOID lpMem);    //释放地址

     f.销毁堆

      BOOL  HeapDestroy( HANDLE hHeap ); //堆句柄

      当堆被销毁后,使用该堆分配的内存全部被销毁

  (3)VirtualAlloc/HeapAlloc/malloc/new在windows平台上函数的调用关系

     malloc/new -> HeapAlloc -> VirtualAlloc

  相关代码:

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"

void HeapInfo()
{
    HANDLE hHeap = GetProcessHeap();
    printf("第一个堆:%p
", hHeap);
    HANDLE hHeaps[256] = { 0 };
    DWORD nCount = GetProcessHeaps(256, hHeaps);
    for (DWORD i = 0; i < nCount; i++)
    {
        printf("%d-%d
", i, hHeaps[i]);
    }
}
void Heap()
{
    //创建1M大小的堆
    HANDLE hHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024 * 1024, 0);
    //获取堆中某部分的地址
    char *pszText = (char*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 2*1024*1024);
    strcpy_s(pszText, 11, "hello heap");
    printf("%s
", pszText);
    //释放内存
    HeapFree(hHeap, 0, pszText);
    //销毁内存
    HeapDestroy(hHeap);
}
int _tmain(int argc, _TCHAR* argv[])
{
    HeapInfo();
    Heap();
    return 0;
}
View Code

  运行结果:

  

5.栈内存

  栈内存:每个线程都具有自己的栈,默认大小为1M。

  一般是系统维护栈,windows提供了_alloca,可在栈上分配内存。

6.内存映射文件(主要作用是进程间通信)

  (1)内存映射文件

     将文件映射成内存来使用,当使用内存时,就是在使用文件。

  (2)内存映射文件的使用

     a.创建或打开文件 CreateFile

     b.创建内存映射文件

      HANDLE CreateFileMapping( HANDLE                            hFile,                        //文件句柄

                                                         LPSECURITY_ATTRIBUTES  lpAttributes,              //安全属性

                                                         DWORD                            flProtect,                   //访问方式

                                                         DWORD                            dwMaximumSizeHigh,  //内存映射文件大小的高32位

                                                         DWORD                            dwMaximumSizeLow,  //内存映射文件大小的di32位

                                                         LPCTSTR                           lpName);                   //命名,可以为NULL(想进程通信,必须给出文件名)

      创建成功返回句柄

  (2)加载内存映射文件(将硬盘文件和本进程地址建立映射关系)

     LPVOID  MapViewOfFile( HANDLE  hFileMappingObject,           //映射文件句柄

                                                DWORD  dwDesiredAcess,               //访问模式

                                                DWORD  dwFileOffsetHigh,               //偏移量的高32位

                                                DWORD  dwFileOffsetLow,                //偏移量的低32位

                                                SIZE_T   dwNumberOfBytesToMap);  //映射的字节数

     成功返回地址

     dwFileOffsetHigh和dwFileOffsetLow是合成的偏移量,必须是区域粒度的整数倍(64K的整数倍)

  (3)使用内存

  (4)卸载内存映射文件(解除映射关系)

     BOOL  UnmapViewOfFile( LPCVOID lpBaseAddress );  //卸载地址

  (5)关闭内存映射文件(删除内存映射文件这个结构)

     CloseHandle

  (6)关闭文件

     CloseHandle

  补充:HANDLE OpenFileMapping ( DWORD    dwDesiredAcess,  //访问方式

                                                     BOOL        bInheritHandle,    //继承标识

                                                     LPCTSTR   lpName);             //内存映射文件名称

     返回内存映射文件句柄

  相关代码:

  (1)写进程

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"

int _tmain(int argc, _TCHAR* argv[])
{
    //创建硬盘文件
    HANDLE hFile = CreateFile("d:/map.dat", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);    
    //创建内存映射文件
    //如果第一个参数为NULL,则申请指定大小的内存
    //如果第一个参数位hFile,将硬盘文件扩至指定大小
    HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, "chan");    
    //得到内存映射文件地址(将本进程地址和内存/硬盘文件建立映射关系)
    char *pszText = (char*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 64 * 1024, 0);    
    strcpy_s(pszText, 10, "hello map");
    printf("%s
", pszText);
    //卸载映射文件
    UnmapViewOfFile(pszText);
    getchar();
    //关闭映射文件
    CloseHandle(hMap);  //一旦关闭就没有了    
    CloseHandle(hFile);
    return 0;
}

  运行结果:

    

  (2)读进程

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"


int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "chan");    
    char *pszText = (char*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 64 * 1024, 0);
    printf("%s
", pszText);
    UnmapViewOfFile(pszText);
    CloseHandle(hMap);
    return 0;
}

  运行结果:

  

  

  

原文地址:https://www.cnblogs.com/csqtech/p/5664958.html