zoukankan      html  css  js  c++  java
  • 第十三章 Windows内存体系结构

    //1.
    每个进程都有自己的虚拟空间,对于32位程序来说,这个地址空间大小是4GB,范围由0X00000000-0XFFFFFFFF,对于64位程序来说,这个地址空间大小是16EB
    在Windows中,正在运行的线程看不到属于操作系统本身的内存,这就意味着他不能无意访问到操作系统的数据
    虽然应用程序有这么大的地址空间,但是这只是虚拟地址空间,为了能正常读写数据,还需要把物理存储器分配或映射到相应的地址空间,否则将导致访问违规
    
    //2.
    每个进程的虚拟地址空间分区:
    
    //3.
    空指针赋值分区:保留该分区的目的是为了帮助程序员捕获对空指针的赋值,如果线程试图读取或写入位于这分区的内存就会应发访问违规,没有API能分配到这一地址区域的虚拟内存
    
    用户模式分区:
    (A):这一分区是进程地址空间的驻地。一个进程无法访问到另一个进程的这一分区的数据
    (B):在Windows中,所有exe和DLL都载入这一区域,系统同时会把进程能访问的所有内存映射文件映射到这一分区
    (C):/LARGEADDRESSAWARE开关:对于Win32程序,此开关默认是关闭的,经测试,若Win32程序打开此开关,则Win32程序能 malloc 到接近4GB内存(当此进程运行在64位系统下才会发生)
    	对于32位程序,打开 /LARGEADDRESSAWARE开关 会限制系统所能创建的线程、栈以及其他资源的数量
    	/LARGEADDRESSAWARE开关:VS2010中设置的方式为:项目->属性->链接器->系统->启用大地址 中进行选择
    #include <Windows.h>
    #include <new>
    #include <vector>
    
    using std::vector;
    using std::nothrow;
    
    int main()
    {
    	vector<char*> vecAlloc;
    	while(1)
    	{
    		auto pRe = (char*)malloc(1024 * 1024);
    		if (!pRe)
    		{
    			break;
    		}
    
    		vecAlloc.emplace_back(pRe);
    		if (vecAlloc.size() > 3800)
    		{
    			break;
    		}
    	}
    
    	std::reverse(vecAlloc.begin(), vecAlloc.end());
    	auto nPoint = *vecAlloc.begin();
    	size_t nMemorySize = vecAlloc.size();	
    
    	vector<HANDLE> vecHandle;
    	while(1)
    	{
    		HANDLE hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    		if (hEvent == NULL || hEvent == INVALID_HANDLE_VALUE)
    		{
    			break;
    		}
    		vecHandle.emplace_back(hEvent);
    		if (vecHandle.size() > 1000000)
    		{
    			break;
    		}
    	}
    	size_t nHandleSize = vecHandle.size();		
    
    	for (size_t i = 0; i < vecAlloc.size(); ++i)
    	{
    		memset(vecAlloc[i], 10, 1024 * 1024);			//访问内存不会出错
    	}
    
    	for (size_t i = 0; i < vecHandle.size(); ++i)
    	{
    		SetEvent(vecHandle[i]);							//内核对象均能触发成功
    		WaitForSingleObject(vecHandle[i], INFINITE);
    	}
    
    	/*
    		Win32开启 /LARGEADDRESSAWARE 开关
    		0xfe800040
    		nMemorySize = 3801
    		nHandleSize = 1000001
    	*/
    
    	printf("
    ");
    }
    
    //疑问:当开启了 /LARGEADDRESSAWARE开关 用户模式下确实得到了接近4GB的内存,那么进程的内核模式分区 不是都被占用完了么,那么为什么进程还能正常运行
    //查资料得:
    The total amount of memory that is available to programs is the amount of physical memory in the computer in addition to the size of the pagefile. 
    An important consideration in the short term is that even 32-bit applications will benefit from increased virtual memory address space when they are running in Windows x64 Editions. 
    Applications that are compiled with the /LARGEADDRESSAWARE option, as would be required to take advantage of the /3GB switch in 32-bit Windows,
    will automatically be able to address 4 GB of virtual memory without any boot time switches or changes to x64 Windows. Plus, of course, the operating system does not have to share that 4 GB of space. 
    Therefore, it is not constrained at all.
    https://support.microsoft.com/en-us/help/294418/comparison-of-32-bit-and-64-bit-memory-architecture-for-64-bit-edition
    
    备注:
    当32位程序运行在32位系统中,只打开 /LARGEADDRESSAWARE 不会让32位进程获得4GB的虚拟地址空间,
    对于上述情况,可以设置3GB开关:在cmd中执行 bcdedit /set IncreaseUserVa 3072 然后再打开进程的 /LARGEADDRESSAWARE 开关,则对应进程可多访问1GB的用户虚拟地址空间
    bcdedit /enum 查看当前BCD各参数设置
    bcdedit /deletevalue IncreaseUserVa 取消对于IncreaseUserVa的设置
    
    (D):为了让64位程序能够访问整个用户模式分区,必须打开/LARGEADDRESSAWARE开关,默认情况下,对于64位程序,此开关是打开的
    (E):不管是32位程序还是64位程序,对于DLL来说,系统都会忽略 /LARGEADDRESSAWARE开关
    
    内核模式分区:
    (A):这一分区是操作系统代码的驻地。与线程调度、内存管理、文件系统支持、网络支持以及设备驱动相关的代码都载入到该分区
    (B):此分区的任何东西为所有进程共有
    (C):应用程序试图读取或写入位于这一分区的内存地址,会应发访问违规
    		
    //4.
    (A):当系统创建一个进程并赋予他地址空间时,可用地址空间的大部分都是闲置的或尚未分配的。为了使用这部分地址空间,必须调用 VirtualAlloc 来分配其中的区域
    (B):当应用程序预定地址空间时,系统会确保区域的起始位置正好是分配粒度(目前所有的CPU平台都使用相同的分配粒度:64KB)的整数倍
    (C):当应用程序预定地址空间的一块区域时,系统会确保区域的大小正好是系统页面大小(x86和x64系统下为4KB, IA-64系统下为8KB)的整数倍
    (D):虽然系统规定应用程序预定的地址空间区域其实位置必须是分配粒度的整数倍,但系统自己不存在这样的限制,但其分配大小也必须是CPU页面的大小的整数倍
    (E):释放预定的地址空间区域,使用 VirtualFree
    
    //5.
    (A):为了使用所预定的地址空间区域,还必须分配物理存储器,并将存储器映射到所预定的区域。物理存储器始终以页面为单位进行调拨,通过 VirtualAlloc 完成这个操作
    (B):当我们调拨物理存储器给区域时,并不需要整个区域都调拨物理存储器
    (C):撤销调拨物理存储器,通过 VirtualFree 完成
    
    //6.
    (A):当今的操作系统能让磁盘空间看起来像内存一样,磁盘上的文件一般称为页交换文件,其中包含虚拟内存,可供任何进程使用
    (B):为了能使用虚拟内存,操作系统需要CPU的大力协助,当线程试图访问存储器中的一个字节时,CPU必须知道该字节是在内存中还是磁盘上
    (C):如果一台机器装备了1GB的内存,硬盘上还存在1GB的页交换文件,那么应用程序会认为可用内存总量是2GB:
    操作系统与CPU分工协作,把内存中的一部分保存到页交换文件中,并在应用程序需要的时候再将页交换文件中的对应部分载入内存。因此使用页交换文件能增加应用程序可用内存总量
    (D):线程访问所属进程的地址空间的一块数据的流程:
    
    (E):系统需要在内存和页交换文件之间复制的页面的频率越高,硬盘颠簸的越厉害,系统运行的越慢
    (F):内存映射文件:把位于硬盘上的文件用作地址空间区域对应的物理存储器
    	当载入一个exe或DLL时,系统会自动预定地址空间区域并把文件映像(exe或DLL)映射本身到该区域,充当物理存储器。这样用于快速加载exe或DLL
    (G):更改页交换文件:控制面板所有控制面板项性能信息和工具高级工具调整Windwos的外观和性能高级更改
    (H):Windows可以使用多个页交换文件,若其位于不同物理磁盘上,那么系统可以运行的更快,因为系统能同时写入多个硬盘
    
    //7.
    页面保护属性
    
    特殊的访问保护属性
    PAGE_NOCACHE:禁止对已调拨的页面进行缓存,一般用于驱动开发使用
    PAGE_WRITECOMBINE:把对单个设备的多次写操作结合在一起以提高性能,一般用于驱动开发
    PAGE_GUARD:使应用程序能在页面中的任一字节被写入时得到通知
    
    //8.
    写时复制:
    (A):页面保护属性中的 PAGE_WRITECOPY PAGE_EXECUTE_WRITECOPY 存在的目的是为了节省内存和页交换文件的使用。Windows 允许多个进程共享同一块存储器,操作系统给共享的存储页指定写时复制属性
    (B):当线程试图写入一个共享页面时,系统会介入并执行以下操作:
    系统在内存中找到一个闲置页面,然后系统把线程想要修改的页面内容复制到找到的闲置页面中,然后系统更新进程的页面表,这样原来的虚拟地址就对应到内存中的一个新页面了,最后进程就可以访问他自己的副本了
    (C):在预订地址空间或调拨物理存储器时,不能使用 PAGE_WRITECOPY 或 PAGE_EXECUTE_WRITECOPY 保护属性。这样会导致 VirtualAlloc 失败。这两个属性是系统在映射exe或DLL时使用的
    
    //9.
    内存区域类型:
    
    块:是一些连续的页面,这些页面具有相同的保护属性,并且以相同类型的物理存储器为后备存储器
    如果同时给区域与物理存储器指定了保护属性,那么以后者为准
    

      

  • 相关阅读:
    简单实现Http代理工具
    Silverlight+WCF 新手实例 象棋 棋子(三)
    Qt for S60 安装
    简单实现Http代理工具完善支持QQ代理
    openSUSE 11.2 初用与上网设置
    简单实现Http代理工具端口复用与QQ代理
    QT 智能提示设置
    Solaris 10 x86 继续折腾Mono
    Silverlight+WCF 新手实例 象棋 介绍(一)
    Qt Creator 运行s60 Emulator
  • 原文地址:https://www.cnblogs.com/szn409/p/8502982.html
Copyright © 2011-2022 走看看