首先看看虚拟内存
虚拟地址空间
32位系统 --- 4GB = 232
64 位系统 ---- 16EB = 264
虚拟内存表
当一个应用程序从硬盘加载到RAM时,首先系统为应用程序保留一定的地址区域,将一些必须的数据加载到内存中,然后将一些不是必须的数据加载到页文件(虚拟内存)中,页文件的大小有操作系统控制。在程序运行过程中,如果需要某个文件,就先去RAM中寻找,如果寻找不到,就去页文件中寻找,如果还寻找不到就发出数据访问违规异常。
过程如下:
每个页文件的保护属性都不同,页文件的页面拥有不同的属性,包括
除了上面介绍的保护属性外,还有3个保护属性标志,即PAGE_NOCACHE,PAGE_WRITECOMBINE和PAGE_GUARD。可以用OR逐位将它们连接,以便将这3个标志用于任何一个保护属性(PAGE_NOCACHE除外)。
第一个保护属性标志PAGE_NOCACHE用于停用已提交页面的高速缓存。一般情况下最好不要使用该标志,因为它主要是供需要处理内存缓冲区的硬件设备驱动程序的开发人员使用的。
第二个保护属性PAGE_WRITECOMBINE也是供设备驱动程序开发人员使用的。它允许把单个设备的多次写入合并在一起,以便提高运行性能。
最后一个保护属性标志PAGE_GUARD可以在页面上写入一个字节时使应用程序收到一个通知(通过一个异常条件)。该标志有一些非常巧妙的用法。Windows2000在创建线程堆栈时使用该标志。
虚拟内存的区域类型共有四个值,即空闲、私有、映像或映射。
数据对齐
数据对齐并不是操作系统的内存结构的一部分,而是CPU结构的一部分。
当CPU访问正确对齐的数据时,它的运行效率最高。当数据大小的数据模数的内存地址是0时,数据是对齐的。
当CPU试图读取的数据值没有正确对齐时,CPU可以执行两种操作之一。即它可以产生一个异常条件,也可以执行多次对齐的内存访问,以便读取完整的未对齐数据值。
X86CPU和AlphaCPU的对齐方式不同。
虚拟内存相关的函数
获取系统信息相关的函数GetSystemInfo()
void WINAPI GetSystemInfo( _Out_ LPSYSTEM_INFO lpSystemInfo//结构体信息 );
结构体如下:
1 typedef struct _SYSTEM_INFO { 2 union { 3 DWORD dwOemId; // Obsolete field...do not use 4 struct { 5 WORD wProcessorArchitecture;//CPU结构 6 WORD wReserved;//保留字段 7 } DUMMYSTRUCTNAME; 8 } DUMMYUNIONNAME; 9 DWORD dwPageSize;//页面大小 10 LPVOID lpMinimumApplicationAddress;//最小内存地址 11 LPVOID lpMaximumApplicationAddress;//最大内存地址 12 DWORD_PTR dwActiveProcessorMask;//哪个CPU是活动的 13 DWORD dwNumberOfProcessors;//CPU数目 14 DWORD dwProcessorType;//CPU类型 15 DWORD dwAllocationGranularity;//保留的内存空间区域的分配粒度, 16 WORD wProcessorLevel; //CPU结构细节 17 WORD wProcessorRevision; //CPU等级 18 } SYSTEM_INFO, *LPSYSTEM_INFO;
其中与内存相关的参数如下:
其他与内存无关的成员函数
现在来看看如何使用这个函数
1 //获取系统参数 2 #include "windows.h" 3 #include "iostream" 4 using namespace std; 5 6 int main() 7 { 8 SYSTEM_INFO sysinfo;//声明一个结构体 9 GetSystemInfo(&sysinfo); 10 11 cout<<"页面大小:"<<sysinfo.dwPageSize<<endl; 12 cout<<"最小内存地址:"<<sysinfo.lpMinimumApplicationAddress<<endl; 13 cout<<"最大内存地址:"<<sysinfo.lpMaximumApplicationAddress<<endl; 14 cout<<"保留的地址空间区域的分配粒度:"<<sysinfo.dwAllocationGranularity<<endl; 15 cout<<"CPU数目:"<<sysinfo.dwNumberOfProcessors<<endl; 16 cout<<"活动CPU:"<<sysinfo.dwActiveProcessorMask<<endl; 17 cout<<"处理器类型:"<<sysinfo.dwProcessorType<<endl; 18 cout<<"处理器结构:"<<sysinfo.wProcessorArchitecture<<endl; 19 cout<<"处理器等级:"<<sysinfo.wProcessorLevel<<endl; 20 cout<<"处理器版本:"<<sysinfo.wProcessorRevision<<endl; 21 22 getchar(); 23 return 0; 24 }
虚拟内存的状态GlobalMemoryStatus()
1 //这是旧版的函数 2 void GlobalMemoryStatus( 3 __out LPMEMORYSTATUS lpBuffer 4 ); 5 typedef struct _MEMORYSTATUS { 6 DWORD dwLength; 7 DWORD dwMemoryLoad; 8 SIZE_T dwTotalPhys; 9 SIZE_T dwAvailPhys; 10 SIZE_T dwTotalPageFile; 11 SIZE_T dwAvailPageFile; 12 SIZE_T dwTotalVirtual; 13 SIZE_T dwAvailVirtual; 14 } MEMORYSTATUS, *LPMEMORYSTATUS;
//新版的函数,支持4G以上虚拟内存,64位宽度
1 //新版函数更加强大 2 BOOL GlobalMemoryStatusEx( 3 __out LPMEMORYSTATUSEX lpBuffer 4 ); 5 typedef struct _MEMORYSTATUSEX { 6 DWORD dwLength;//结构体大小 7 DWORD dwMemoryLoad;//已加载的内存比例 8 DWORDLONG ullTotalPhys;//物理内存大小 9 DWORDLONG ullAvailPhys;//当前可用物理内存大小 10 DWORDLONG ullTotalPageFile;//总的页面大小 11 DWORDLONG ullAvailPageFile;//当前可用的页面大小 12 DWORDLONG ullTotalVirtual;// 每个进程的地址空间中私有的总字节数 13 DWORDLONG ullAvailVirtual;// 进程的空闲地址空间的数量 14 DWORDLONG ullAvailExtendedVirtual;//保留,0 15 } MEMORYSTATUSEX, *LPMEMORYSTATUSEX;
注意事项:
在调用GlobalMemoryStatus之前,必须将dwLength成员初始化为结构体的大小,即一个MEMORYSTATUS结构的大小。
函数使用如下:
1 //获取虚拟内存状态 2 cout<<"\n虚拟内存状态\n"<<endl; 3 //虚拟内存状态 4 MEMORYSTATUSEX ms; 5 ms.dwLength = sizeof(MEMORYSTATUSEX);//这个参数必须首先初始化 6 GlobalMemoryStatusEx(&ms); 7 cout<<"结构体大小:"<<ms.dwLength<<endl; 8 cout<<"已加载的内存比例:"<<ms.dwMemoryLoad<<endl; 9 cout<<"物理内存大小:"<<ms.ullTotalPhys<<endl; 10 cout<<"可用内存大小:"<<ms.ullAvailPhys<<endl; 11 cout<<"总的页面数:"<<ms.ullTotalPageFile<<endl; 12 cout<<"可用页面数:"<<ms.ullAvailPageFile<<endl; 13 cout<<"每个进程的地址空间中私有的总字节数:"<<ms.ullTotalVirtual<<endl; 14 cout<<"进程的空闲地址空间的数量:"<<ms.ullAvailVirtual<<endl;
地址空间状态VirtualQuery()
通过该函数或获取进程地址空间的信息,如大小,存储类型,保护属性等。
1 SIZE_T WINAPI VirtualQuery( 2 _In_opt_ LPCVOID lpAddress,//要查询的进程的地址空间信息的虚拟内存地址 3 _Out_ PMEMORY_BASIC_INFORMATION lpBuffer,//结构体 4 _In_ SIZE_T dwLength//本结构体的大小 5 ); 6 //其中参数的详细情况如下 7 typedef struct _MEMORY_BASIC_INFORMATION { 8 PVOID BaseAddress;//进程地址 9 PVOID AllocationBase;//基地址 10 DWORD AllocationProtect;//保护属性 11 SIZE_T RegionSize;//所有页面大小 12 DWORD State;//相邻界面状态 13 DWORD Protect;//相邻界面保护属性 14 DWORD Type;//相邻界面的物理存储器类型 15 } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
参数介绍如下:
由一个进程查询另一个进程的信息 VirtualAllocEx()
调试程序会调用这个函数,查看其他进程的信息
1 //由一个进程查询另一个进程的信息 2 LPVOID VirtualAllocEx( 3 __in HANDLE hProcess,//进程句柄 4 __in_opt LPVOID lpAddress,//查询地址 5 __in SIZE_T dwSize,//与分配的页面大小 6 __in DWORD flAllocationType,//分配类型 7 __in DWORD flProtect //保护属性 8 );
如何查询地址状态
1 //如何查询地址状态 2 cout<<"地址空间的状态"<<endl; 3 MEMORY_BASIC_INFORMATION mbi; 4 HANDLE hProcess = NULL; 5 TCHAR path[1024] = {0}; 6 DWORD dwResult = VirtualQuery(hProcess,&mbi,sizeof(mbi)); 7 while(dwResult) 8 { 9 cout<<"内存地址起始地址:"<<hProcess<<endl; 10 switch(mbi.State) 11 { 12 case MEM_COMMIT: 13 cout<<"已经物理分配"<<endl; 14 break; 15 case MEM_RESERVE: 16 cout<<"保留"<<endl; 17 break; 18 case MEM_FREE: 19 cout<<"空闲没有分配"<<endl; 20 break; 21 default: 22 break; 23 } 24 switch(mbi.Type) 25 { 26 case MEM_IMAGE: 27 cout<<"该地址为文件镜像"<<endl; 28 GetModuleFileName((HINSTANCE)hProcess,path,1024); 29 cout<<path<<endl; 30 break; 31 case MEM_MAPPED: 32 cout<<"已经映射了"<<endl; 33 break; 34 case MEM_PRIVATE: 35 cout<<"私有的空间"<<endl; 36 break; 37 default: 38 break; 39 } 40 getchar(); 41 cout<<endl; 42 hProcess=(PBYTE)hProcess+mbi.RegionSize; 43 dwResult=VirtualQuery(hProcess,&mbi,sizeof(MEMORY_BASIC_INFORMATION)); 44 }
1. WinDbg介绍:
Debugging Tools and Symbols: Getting Started
http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx
A word for WinDbg
http://mtaulty.com/communityserver/blogs/mike_taultys_blog/archive/2004/08/03/4656.aspx
2. WinDbg下载:
Install Debugging Tools for Windows 32-bit Version
http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
Install Debugging Tools for Windows 64-bit Versions
http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx
3. 配置WinDbg:
运行WinDbg->菜单->File->Symbol File Path->按照下面的方法设置_NT_SYMBOL_PATH变量:
在弹出的框中输入“C:\MyCodesSymbols; SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols”(按照这样设置,WinDbg将先从本地文件夹C:\MyCodesSymbols中查找Symbol,如果找不到,则自动从MS的Symbol Server上下载Symbols)。另一种做法是从这个Symbol下载地址中http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx,下载相应操作系统所需要的完整的Symbol安装包,并进行安装,例如我将其安装在D:\WINDOWS\Symbols,在该框中输入“D:\WINDOWS\Symbols”。(这里要注意下载的Symbols的版本一定要正确,在我的Win2003+Sp1上,我曾经以为安装Win2003+Sp2的Symbols可能会牛×点,但结果证明我错了,用WinDbg打开可执行文件时,提示“PDB symbol for mscorwks.dll not loaded;Defaulted to export symbols for ntdll.dll”的错误,我有重新装上Win2003+Sp1的Symbols, 现在一切运行正常^_^)
4. 使用WinDbg:
WinDbg提供了图形界面和命令行两种运行方式。这里介绍使用图形界面的WinDbg来调试应用程序:
File->OpenExecutable->可以选择一个可执行文件进行调试;
File->Attache to a Process->可以选择一个运行中的进程,并对其进行调试;
至此,我们就可以在上图中用红色方框标记的文本框中输入各个功能指令了(有关指令的帮助文档,可以参考:Help->Contents-> Debugging Tools for Windows->Debuggers->Debugger Reference,该目录下列集了所有指令机器功能说明!)。
http://blog.csdn.net/bluebridge/archive/2007/05/10/1602749.aspx
WinDbg是微软开发的免费源码级调试工具。Windbg可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。在DotNet方面WinDbg是调试分析SSCLI的最佳工具,同时可以和SOS扩展一起调试分析.net程序。最近一段时间项目需要学习了一下,有感于WinDbg功能的强大。学习的过程并不一帆风顺,WinDbg自带的英文文档大而泛,网上资料也比较少,而WinDbg Not an ABBA Song。现在总算是入了点门,也收集了一些很好的资料,整理成一个CHM方便大家学习,时间仓促,整理得比较粗糙,大家凑合着看。这些文章均来自网络,版权归原作者所有。
下面三个包一个是我整理的文章,下面两个是WinDbg方面很不错的教程。
我整理的文章
windbg基础
kernel debug教程