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;
}       

 

posted @ 2012-08-20 09:14  唐小喵  阅读(419)  评论(0编辑  收藏  举报