zoukankan      html  css  js  c++  java
  • 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;
    }

      运行结果:

      

      

      

  • 相关阅读:
    26.列表的循环遍历
    效率比较--链表
    心之距离 再回首 光年之遥远
    效率比较--集合
    效率比较--数组
    哈希表
    栈 VS 队列
    struts1 标签引入
    web 连接池配置
    zip&ftp命令
  • 原文地址:https://www.cnblogs.com/csqtech/p/5664958.html
Copyright © 2011-2022 走看看