zoukankan      html  css  js  c++  java
  • Win32编程day13 学习笔记

    Windows的内存管理

    一 地址空间

      1 地址空间
     
      一个程序最大的寻址范围。对于Win32操作系统最大的寻址范围是2的32次方,0-0xFFFFFFFF。这个寻址范围由CPU决定。CPU的寻址范围越大,编程难度降低。
     
      2 地址空间的划分
        通常情况下:
        2.1 用户空间
           地址范围 0 - 0x7FFFFFFF(2G),运行应用程序代码、数据等等。
           2.2.1 空指针区(NULL区)
             地址范围 0 - 0x0000FFFF
           2.2.2 用户区
             地址范围 0x00010000 - 0x7FFEFFFF
           2.2.3 64K禁入区
             地址范围 0x7FFEFFFF - 0x7FFFFFFF
        2.2 内核空间
           地址范围 0x80000000 - 0xFFFFFFFF,被系统使用,运行驱动、内核的数据和代码。
     
    二 地址映射

      1 区域
        区域指一段连续的地址空间,区域的粒度和CPU的粒度、操作系统相关。目前通常都是以64K粒度存在,地址的对齐方式是以64K为边界。
        区域的状态:
          1)空闲 - 空闲的,可以被使用
          2)私有 - 已经被占有,但是还未使用
          3)映像 - 程序的代码使用
          4)映射 - 程序的数据使用
         
      2 物理内存
        实际可以使用的物理存储器。
       
     3 虚拟内存
       使用硬盘空间作为内存扩展,也可以当作物理内存使用。
      
     4 内存页
      操作系统使用内存页的方式管理物理内存和虚拟内存。通常情况下,内存页的大小为4K或者8K。
      每个内存页具有自己的状态,例如  只读/可写/可执行
       
      5 页目表
        用于管理内存页的表。
        页目  - 页表   - 内存页
                       - 内存页
              - 页表
              - 页表
             
        指针 31 -----22 21-------12 11-----------0
                页目       页表         偏移量
               
      6 地址空间的访问
       
        6.1 地址空间已经存在映射好的物理内存,直接使用,返回。
        6.2 系统去虚拟内存中,查找对应的内存页。如果未找到,系统错误返回。
        6.3 系统将虚拟内存的内存页切换到物理内存当中。
        6.4 返回实际物理内存地址,使用数据。

    View Code
    // WinSys.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "windows.h"
    
    void ShowSys( )
    {
        SYSTEM_INFO info = { 0 };
        GetSystemInfo( &info );
        printf( "内存页的大小: %d\n", 
            info.dwPageSize );
        printf( "可用最小地址: %p\n", 
            info.lpMinimumApplicationAddress );
        printf( "可用最大地址: %p\n", 
            info.lpMaximumApplicationAddress );
        printf( "区域的分配粒度: %d\n", 
            info.dwAllocationGranularity );
    }
    
    int main(int argc, char* argv[])
    {
        ShowSys( );
        //错误的地址
        //printf( "%d", *((int *)0x0000FFFF) );
        return 0;
    }

      7 内存的使用
        7.1 虚拟内存
           适合对于大内存分配使用。一般情况下如果分配的内存大于1M,应该使用虚拟内存分配方式。
        7.2 堆内存
           适合对于小内存分配使用。一般情况下对于小于1M的内存分配使用。例如
           malloc/new。
        7.3 堆栈内存
           系统维护的内存区。   
          
    二 虚拟内存
      1 虚拟内存
        常用于大内存分配,分配的速度快,可以根据需要指定分配方式。
      2 虚拟内存的使用
        2.1 分配内存

        LPVOID VirtualAlloc(
            LPVOID lpAddress,//NULL或者用于提交的内存地址
        DWORD dwSize,//分配的大小,一般是页倍数
        DWORD flAllocationType,//分配的方式
        DWORD flProtect );//内存访问方式

        分配的最大空间小于用户区间(通常是2G)。
        2.2 提交内存
           VirtualAlloc使用MEM_COMMIT方式。例如:

               pszBuf = (CHAR *)VirtualAlloc( 
                      pszBuf, //需要提交内存地址
                        1024 * 1024 * 1024,
                        MEM_COMMIT,
                        PAGE_READWRITE );

        2.3 使用内存
        2.4 释放内存

        BOOL VirtualFree(
           LPVOID lpAddress,//释放的内存
           DWORD dwSize, //释放的大小
           DWORD dwFreeType ); //释放的方式

      3 内存信息

        VOID GlobalMemoryStatus(
        LPMEMORYSTATUS lpBuffer  //获取内存信息
        ); 
    View Code
    // WinVirtual.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "conio.h"
    #include "windows.h"
    
    void Status( )
    {    //获取内存信息
        MEMORYSTATUS status = { 0 };
        status.dwLength = sizeof( status );
        GlobalMemoryStatus( &status );
        printf( "TotalPhys: %u\n",     status.dwTotalPhys );
        printf( "AvailPhys: %u\n",     status.dwAvailPhys );
        printf( "TotalPageFile: %u\n", status.dwTotalPageFile );
        printf( "AvailPageFile: %u\n", status.dwAvailPageFile );
        printf( "TotalVirtual: %u\n",  status.dwTotalVirtual );
        printf( "AvailVirtual: %u\n",  status.dwAvailVirtual );
        printf( "MemoryLoad: %d\n",    status.dwMemoryLoad );
    }
    
    void Virtual( )
    {    
        Status( );
        //地址分配
        CHAR * pszBuf = (CHAR *)
            VirtualAlloc( NULL, 
            1024 * 1024 * 1024,
            MEM_RESERVE,
            PAGE_READWRITE );
        printf( "MEM_RESERVE: %p\n", pszBuf );
        
        Status( );
    
        getch( );
        //内存提交
        pszBuf = (CHAR *)
            VirtualAlloc( pszBuf,
            1024 * 1024 * 1024,
            MEM_COMMIT,
            PAGE_READWRITE );
        printf( "MEM_COMMIT: %p\n", pszBuf );
        Status( );
    
        strcpy( pszBuf, "hello Virtual" );
        printf( "%s\n", pszBuf );
        
        Status( );
        getch( );
    
        //释放内存
        VirtualFree( pszBuf, 
            1024 * 1024 * 1024, 
            MEM_RELEASE );
    }
    
    int main(int argc, char* argv[])
    {
        Virtual( );
        return 0;
    }

    三 堆内存

      1 堆内存的特点
        一般分配小数据内存,一般小于1M数据使用堆内存分配。
        一般程序执行后,会有一个默认堆,这个堆的大小一般为1M。一个程序可以多个堆。通过堆内存管理器来管理堆中的内存。
        内存分配速度比VirtualAlloc慢。
       
      2 堆内存的使用
        2.1 创建堆

          HANDLE HeapCreate(
          DWORD flOptions,//创建标示
          DWORD dwInitialSize,  //初始化大小
          DWORD dwMaximumSize ); //最大大小

        2.2 分配内存

          LPVOID HeapAlloc(
          HANDLE hHeap,  //堆的句柄
          DWORD dwFlags, //分配标示
          DWORD dwBytes );  //分配大小

        2.3 使用内存
        2.4 释放内存

          BOOL HeapFree(
          HANDLE hHeap,  //堆的句柄
          DWORD dwFlags, //释放标示
          LPVOID lpMem );  //释放的地址

        2.5 释放堆

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

      3 malloc/HeapAlloc/VirtualAlloc
        malloc内部调用HeapAlloc。
        HeapAlloc内部调用的VirtualAlloc。
        malloc分配内存:
         例如100字节
         | 内存头 | 100字节 | 4字节尾部标示 |
         所用使用malloc分配的内存,会使用这个内存头构成链表.
        
      4 堆的信息
        GetProcessHeap 当前进程默认堆的句柄
        GetProcessHeaps 当前进程所有堆的句柄

    View Code
    // WinHeap.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "stdlib.h"
    #include "windows.h"
    
    void HeapInfo( )
    {    //默认堆的句柄
        HANDLE hHeap = GetProcessHeap();
        printf( "Default Heap: %p\n", hHeap );
        //所有的堆的句柄
        HANDLE hHeaps[256] = { 0 };
        DWORD nCount = 
            GetProcessHeaps( 256, hHeaps );
        printf( "All Heap: %d\n", nCount );
        for( DWORD nIndex=0; nIndex<nCount; nIndex++ )
        {
            printf( "\t%d: %p\n", nIndex+1,
                hHeaps[nIndex] );
        }
    }
    
    void Heap( )
    {
        HeapInfo( );
    
        //创建堆
        HANDLE hHeap = HeapCreate( 
            HEAP_GENERATE_EXCEPTIONS,
            1024 * 1024, 0 );
        printf( "HeapCreate: %p\n", hHeap );
    
        HeapInfo( );
    
        //内存分配
        CHAR * pszBuf = ( CHAR * )
            HeapAlloc( hHeap, HEAP_ZERO_MEMORY, 100 );  //分配后初始化为0
        printf( "HeapAlloc: %p\n", pszBuf );
    
        strcpy( pszBuf, "hello Heap" );
        printf( "%s\n", pszBuf );
    
        //内存释放
        HeapFree( hHeap, 0, pszBuf );
    
        //释放堆
        HeapDestroy( hHeap );
    
        HeapInfo( );
    }
    
    int main(int argc, char* argv[])
    {
        CHAR * pszBuf = (CHAR *)malloc( 1024 );
    
        Heap( );
        return 0;
    } 

    四 堆栈内存
      堆栈都是小数据的使用,由系统维护,栈的大小一般在1M左右.
      例如,Windows下可以使用_alloca函数从栈上分配内存.很少会用
     
     
    五 内存映射文件

      1 内存映射文件
        可以将文件映射成内存,我们可以像使用内存一样使用文件.
       
      2 内存映射文件的使用
        2.1 创建或打开一个文件
          CreateFile
        2.2 创建内存映射文件

          HANDLE CreateFileMapping(
              HANDLE hFile, //文件句柄
          LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//安全属性
          DWORD flProtect, //保护模式
          DWORD dwMaximumSizeHigh,//大小的高32位
          DWORD dwMaximumSizeLow,    //大小的低32位
          LPCTSTR lpName ); //文件映射内核对象的名称

        2.3 映射成内存地址

             LPVOID MapViewOfFile(
           HANDLE hFileMappingObject, //文件映射句柄
           DWORD dwDesiredAccess, //访问模式
           DWORD dwFileOffsetHigh, //地址偏移高32位
           DWORD dwFileOffsetLow,//地址偏移低32位
           DWORD dwNumberOfBytesToMap ); //要映射的字节数

        2.4 使用内存
        2.5 卸载映射

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

        2.6 关闭内存映射文件
           CloseHandle
        2.7 文件关闭
           CloseHandle

    View Code
    // WinMap.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "windows.h"
    
    void Map( )
    {    
        //创建文件
        HANDLE hFile = CreateFile( "C:\\map.dat",
            GENERIC_READ|GENERIC_WRITE,
            0, NULL, CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL, NULL );
        //创建文件映射
        HANDLE hMap = CreateFileMapping( hFile, NULL,
            PAGE_READWRITE, 0, 1024 * 1024, NULL );
        //映射地址
        CHAR * pszText = (CHAR *)MapViewOfFile( 
            hMap, FILE_MAP_ALL_ACCESS,
            0, 0, 1024 * 1024 );
        //使用内存
        strcpy( pszText, "Hello File Mapping" );
        printf( "%s\n", pszText );
        //卸载地址
        UnmapViewOfFile( pszText );
        //关闭文件映射
        CloseHandle( hMap );
        //关闭文件
        CloseHandle( hFile );
    }
    
    int main(int argc, char* argv[])
    {
        Map( );
        return 0;
    }       
  • 相关阅读:
    web api 特点
    码农
    到程序员短缺的地方生活,不要到过剩的地方凑热闹
    程序员也要寻找贸易的机会,要参加研讨会
    [Codeforces 863D]Yet Another Array Queries Problem
    [Codeforces 863C]1-2-3
    [Codeforces 864F]Cities Excursions
    [Codeforces 864E]Fire
    [Codeforces 864D]Make a Permutation!
    [Codeforces 864C]Bus
  • 原文地址:https://www.cnblogs.com/tangzhengyue/p/2647033.html
Copyright © 2011-2022 走看看