句柄是什么?Windows结构体里面句柄的作用( 二 )


这种虚拟内存的好处是很多的,这里以连续内存分配和可移动内存为例来讲一讲 。
首先说一说连续内存分配,我们在程序中经常需要分配一块连续的内存结构,如数组,他们可以使用指针循环读取,但是物理内存多次分配释放后实际上是破碎的,如下图

句柄是什么?Windows结构体里面句柄的作用

文章插图
图中白色为可用物理内存,黑色为被其他程序占有的内存,现在要分配一个12大小的连续内存,那么显然物理内存中是没有这么大的连续内存的,这时候通过页表对应的方式可以看到我们很容易得到逻辑地址上连续的12大小的内存 。
再说一说可移动内存,我们使用GlobalAlloc等函数时,经常会指定GMEM_MOVABLE和GMEM_FIXED参数,很对人对这两个参数很头疼,搞不明白什么意思 。
实际上这里的MOVABLE和FIXED都是针对的逻辑地址来说的 。GMEM_MOVABLE是说允许操作系统(或者应用程序)实施对内存堆(逻辑地址)的管理,在必要时,操作系统可以移动内存块获取更大的块,或者合并一些空闲的内存块,也称“垃圾回收”,它可以提高内存的利用率,这里的地址都是指逻辑地址 。同样以分配12大小连续的内存,在某种状态时,内存结构如下
句柄是什么?Windows结构体里面句柄的作用

文章插图
显然这时候是无法分配12连续大小的内存,但是如果这里的逻辑地址都指明为GMEM_MOVABLE的话,操作系统这时候会对逻辑地址做管理,得到如下结果:
句柄是什么?Windows结构体里面句柄的作用

文章插图
这时候就实现了逻辑地址的MOVE,相对比实现物理内存的移动,这样的代价当然要小得多撒,但是聪明的小伙伴们是不是要问,这样在逻辑地址中移动了内存,那么实际访问数据不都乱套了吗,还能找到自己分配的实际物理内存数据吗,等等,不要心急,这就是等下要讲的句柄做的事情了 。
GMEM_FIXED是说允许在物理内存中移动内存块,但是必须保证逻辑地址是不变的,在早期16位Windows操作系统不支持在物理内存中移动内存,所以禁止使用GMEM_FIXED,现在的你估计体会不到了 。
事实上用GlobalAlloc分配内存时指定GMEM_FIXED参数返回的句柄就是指向内存分配的内存块的指针,不理解???接着看下面的句柄结构,你就明白了 。
二、句柄结构
在上面讲解虚拟内存结构的过程中,我们就引出了几个问题:MOVABLE的内存访问为什么不会乱,FIXED的内存为什么说就是指向分配内存块的指针 。
事实上我们尽管Windows没有给出源码,但是从一些头文件、MSDN和Windows早期内存分配函数中我们还是可以一窥端倪 。
在Winnt.h头文件中做了通用句柄的定义:
01#ifdef STRICT02typedef void *HANDLE;03#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name04#else05typedef PVOID HANDLE;06#define DECLARE_HANDLE(name) typedef HANDLE name07#endif08typedef HANDLE *PHANDLE;复制代码#ifdef STRICTtypedef void *HANDLE;#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name#elsetypedef PVOID HANDLE;#define DECLARE_HANDLE(name) typedef HANDLE name#endiftypedef HANDLE *PHANDLE;在Windef.h做了特殊句柄的定义:
01#if !defined(_MAC) || !defined(GDI_INTERNAL)02DECLARE_HANDLE(HFONT);03#endif04DECLARE_HANDLE(HICON);05#if !defined(_MAC) || !defined(WIN_INTERNAL)06DECLARE_HANDLE(HMENU);07#endif08DECLARE_HANDLE(HMETAFILE);09DECLARE_HANDLE(HINSTANCE);10typedef HINSTANCE HMODULE;/* HMODULEs can be used in place of HINSTANCEs */11#if !defined(_MAC) || !defined(GDI_INTERNAL)12DECLARE_HANDLE(HPALETTE);13DECLARE_HANDLE(HPEN);14#endif15DECLARE_HANDLE(HRGN);16DECLARE_HANDLE(HRSRC);17DECLARE_HANDLE(HSTR);18DECLARE_HANDLE(HTASK);19DECLARE_HANDLE(HWINSTA);20DECLARE_HANDLE(HKL);复制代码#if !defined(_MAC) || !defined(GDI_INTERNAL)DECLARE_HANDLE(HFONT);#endifDECLARE_HANDLE(HICON);#if !defined(_MAC) || !defined(WIN_INTERNAL)DECLARE_HANDLE(HMENU);#endifDECLARE_HANDLE(HMETAFILE);DECLARE_HANDLE(HINSTANCE);typedef HINSTANCE HMODULE;/* HMODULEs can be used in place of HINSTANCEs */#if !defined(_MAC) || !defined(GDI_INTERNAL)DECLARE_HANDLE(HPALETTE);DECLARE_HANDLE(HPEN);#endifDECLARE_HANDLE(HRGN);DECLARE_HANDLE(HRSRC);DECLARE_HANDLE(HSTR);DECLARE_HANDLE(HTASK);DECLARE_HANDLE(HWINSTA);DECLARE_HANDLE(HKL);这里微软把通用句柄HANDLE定义为void指针,显然啦,他是不想让人知道句柄的真实类型,但是和他以往的做法一样,微软空有一个好的想法结果没有实现 。马上,如果定义了强制类型检查STRICT,他又定义了特殊类型句柄宏DECLARE_HANDLE,这里用到了##,这是比较偏僻的用法,翻译过来,对于诸如DECLARE_HANDLE(HMENU)定义其实就是

推荐阅读