精华内容
下载资源
问答
  • 这是windows10的一个问题,反反复复的出现(4核 8G内存) 那我们现在来看看如何处理。 根据官方给出的最低配置要求:1G就可以了(当然这是理论值,我们这边就算2G,开机什么都不做占2G内存,也就25%的内存是吧,如果...

    这是windows10的一个问题,反反复复的出现(4核 8G内存)
    那我们现在来看看如何处理。
    根据官方给出的最低配置要求:1G就可以了(当然这是理论值,我们这边就算2G,开机什么都不做占2G内存,也就25%的内存是吧,如果高于这个可能就不正常了!)

    1:主要用于一时的解决方案
    win键—调出小娜—查询内存诊断—点击内存诊断
    内存诊断时间大断要30分钟以内,诊断完60%降为25%左右。
    这种方法只能解决一时,没过几天,就会出现这种情况。

    2:主要用于开机占用超过60%内存现象:
    完全禁用superfetch:预加载功能。简单解释一下:就是根据用户操作习惯把经常使用的程序,预先加载(感觉有点坑,要不你让我们自己选择呗!特别是硬盘为SSD的情况下,根本没太大作用!),关闭这玩意儿,没任何副作用,可以放心关闭。关闭方法如下:
    Win键+R—输入services.msc,点enter键— 找到superfetch服务,双点服务,选择禁用,点击停止,最后点确定,就搞定了。
    这步完全有必要,可能选择完没太多明显提升,但是长期是有很大的优化。
    等同于:(修改注册表–比较危险,不懂的最好不要操作)
    Windows Registry Editor Version 5.00
    [HKEY_LOCAL_MACHINESYSTEMControlSet001ServicesTimeBroker]
    “Start”=dword:00000003
    [HKEY_LOCAL_MACHINESYSTEMControlSet001ServicesSysMain]
    “DisplayName”=“Superfetch”
    “Start”=dword:00000003

    3:关闭虚拟内存:有效降低了内存持续增长现象
    右击“计算机”选择属性,打开之后选择“高级系统设置”
    点击“性能”中的设置按钮;
    选择“高级”,点击虚拟内存中的“更改”,将“自动管理驱动器的分页文件大小”对勾去掉,点击下面的“系统管理的大小”,点击“设置”并“确定”即可。

    在线观看视频更通彻!

    windows10内存占用高并一直增长

    展开全文
  • 1、了解Windows内存管理机制,了解页式存储管理技术。 2、熟悉Windows内存管理基本数据结构。 3、使用Windows内存管理基本API的使用 二、实验要求 1、使用Windows系统提供的函数和数据结构显示系统存储空间的使用...

    实验题目:系统内存使用统计
    一、实验目的
    1、了解Windows内存管理机制,了解页式存储管理技术。
    2、熟悉Windows内存管理基本数据结构。
    3、使用Windows内存管理基本API的使用
    二、实验要求
    1、使用Windows系统提供的函数和数据结构显示系统存储空间的使用情况。
    2、内存和虚拟存储空间变化时,给出系统显示变化结果。
    3、分析实验结果。

    入坑(全网都是这一个的改编)

    错误代码(但大家都在用):

    #include "stdio.h"
    #include "windows.h"
    
    void MemoryStatus(void)                             //统计内存的状态
    {
    	MEMORYSTATUS lpmemory;
    
    	GlobalMemoryStatus(&lpmemory);//此函数用来获得当前可用的物理和虚拟内存信息
    
    	printf("物理内存总大小: %dMB\n", lpmemory.dwTotalPhys / (1024 * 1024));
    	printf("可用物理内存: %dMB\n", lpmemory.dwAvailPhys / (1024 * 1024));
    	printf("页文件总大小: %dMB\n", lpmemory.dwTotalPageFile / (1024 * 1024));
    	printf("可用页文件大小: %dMB\n", lpmemory.dwAvailPageFile / (1024 * 1024));
    	printf("虚拟地址空间总大小: %dMB\n", lpmemory.dwTotalVirtual / (1024 * 1024));
    	printf("可用虚拟地址空间大小:%dMB\n", lpmemory.dwAvailVirtual / (1024 * 1024));
    	printf("内存加载率: %d%% \n", lpmemory.dwMemoryLoad);
    }
    
    int main()
    {
    
    	LPVOID  Virtualbase;
    	char *string;
    	printf("================当前内存的使用情况================\n");
    	MemoryStatus();//获取当前的内存状态
    	printf("\n\n现在开始分配10MB物理内存和100MB虚拟内存\n\n");
    	string = (char *)malloc(10 * 1024 * 1024); //分配物理内存
    	Virtualbase = VirtualAlloc(NULL, 100 * 1024 * 1024 , MEM_COMMIT, PAGE_READWRITE);             //分配虚拟内存
    	if (Virtualbase == NULL)	printf("虚拟内存分配失败\n");
    	
    	printf("================分配完之后的内存状态如下================\n");
    	MemoryStatus();
    	printf("\n\n现在开始分配10MB物理内存和100MB虚拟内存\n\n");
    	free(string);  //释放物理内存
    	if (VirtualFree(Virtualbase, 0, MEM_RELEASE)==0) //释放虚拟内存
    		printf("释放虚拟内存失败!!\n");
    	
    	printf("================释放完之后的内存状态如下================\n");
    	MemoryStatus();
    
    	return 0;
    
    }
    

    运行结果:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    结果分析:
    (1)对于物理内存,我选择分配10MB(对于MB分配的时候,这个单位应该是1024*1024),但是分配之后的结果出现了三种:大于、小于、等于10MB的都有;另外,释放完之后一般是回不到最初的状态。
    (2)对于虚拟内存,分配的是100MB,结果显示分配的大小总是:分配的虚拟内存+分配的物理内存,释放完之后都能回到最初的状态。
    这已经出现问题了,完全不符合预期结果?!是的,真的达不到预期!!!
    即使从分页管理方式碎片、用户行为不能对内存修改等这样的角度,总有说不通的地方。

    出坑尝试

    从老师那里得知,malloc申请的是虚拟内存,因此,尝试将malloc换成new。
    new的正确打开方式:

    char *string=new char[10 * 1024 * 1024];
    

    释放new的正确打开方式:

    delete string;
    

    观察运行结果:
    在这里插入图片描述
    事实上,和malloc没什么区别。
    原来new在Windows中也是在堆中分配,应该也是虚拟内存。

    出坑的正确打开方式

    最简单的方法就是:去了解windows系统的内存管理机制
    (不过,网上这方面的资料真的不多,原创的更是少)
    这里贴出来查到的一些优质文章:
    全面介绍Windows内存管理机制及C++内存分配实例(一):进程空间
    全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询
    全面介绍Windows内存管理机制及C++内存分配实例(三):虚拟内存
    改后的代码:

    #include<stdio.h>
    #include<Windows.h>
    #include<iostream>
    using namespace std;
    
    //尝试调用Windows API激活Lock Pages in Memory权限
    BOOL LoggedSetLockPagesPrivilege(HANDLE hProcess, BOOL bEnable)
    {
    	struct {
    		DWORD Count;//数组的个数
    		LUID_AND_ATTRIBUTES Privilege[1];
    	} Info;
    
    	HANDLE Token;
    
    	//打开本进程的权限句柄
    	BOOL Result = OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES,&Token);
    	if(Result != TRUE)
    	{
    		printf("不能打开进程的权限句柄\n");
    		return FALSE;
    	}
    	else
    	{
    		printf("能打开进程的权限句柄\n");
    	}
    
    	//只改变一个属性
    	Info.Count = 1;
    	//准备激活
    	if (bEnable)
    		Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
    	else
    		Info.Privilege[0].Attributes = 0;
    
    	//根据权限名字找到LGUID
    	Result = LookupPrivilegeValue(NULL,SE_LOCK_MEMORY_NAME,&(Info.Privilege[0].Luid));
    	if (Result != TRUE)
    	{
    		printf("无法获取内存中锁定页SE_LOCK_MEMORY_NAME的权限\n");
    		return FALSE;
    	}
    	else
    		printf("可获取内存中锁定页SE_LOCK_MEMORY_NAME的权限\n");
    
    	// 激活Lock Pages in Memory权限
    	Result = AdjustTokenPrivileges(Token, FALSE, (PTOKEN_PRIVILEGES)&Info, 0, NULL, NULL);
    	if (Result != TRUE)
    	{
    		printf("不能调整进程句柄权限", GetLastError());
    		return FALSE;
    	}
    	else
    	{
    		if (GetLastError() != ERROR_SUCCESS)
    		{
    			printf("不能使用内存中锁定页SE_LOCK_MEMORY_NAME权限\n");
    			printf("请检查本地权限\n");
    			printf("\n");
    			return FALSE;
    		}
    
    	}
    	CloseHandle(Token);
    	return TRUE;
    }
    
    
    //内存和虚拟存储空间变化情况
    void D_F()
    {
    
    	const int content=100;
    	MEMORYSTATUS memStatusVirtual;
    	GlobalMemoryStatus(&memStatusVirtual);
    
    	PVOID pVirtual = VirtualAlloc(NULL, content * 1024 * 1024, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
    	if (pVirtual == NULL)
    		cout << "没有那么大连续进程空间!" << endl;
    
    	MEMORYSTATUS memStatusVirtual2;
    	GlobalMemoryStatus(&memStatusVirtual2);
    
    	cout << "=========分配虚拟内存(可用进程空间)=========" << endl;
    	cout << "减少物理内存=" << memStatusVirtual.dwAvailPhys - memStatusVirtual2.dwAvailPhys << endl;
    	cout << "减少可用页文件=" << memStatusVirtual.dwAvailPageFile - memStatusVirtual2.dwAvailPageFile << endl;
    	cout << "减少可用进程空间="
    		<< memStatusVirtual.dwAvailVirtual - memStatusVirtual2.dwAvailVirtual << endl;
    	cout << endl;
    	if (VirtualFree(pVirtual, 0, MEM_RELEASE) == 0)//释放虚拟内存
    		printf("释放虚拟内存失败\n");
    	
    	MEMORYSTATUS memStatusVirtual3;
    	GlobalMemoryStatus(&memStatusVirtual3);
    	cout << "=========释放虚拟内存(可用进程空间)=========" << endl;
    	cout << "增加可用进程空间="
    		<< memStatusVirtual3.dwAvailVirtual- memStatusVirtual2.dwAvailVirtual << endl << endl;
    	cout << endl;
    
    	LoggedSetLockPagesPrivilege(GetCurrentProcess(), true);//尝试调用Windows API激活Lock Pages in Memory权限
    
    	//物理内存的变化情况
    	SYSTEM_INFO sysInfo;
    	GetSystemInfo(&sysInfo);
    
    	ULONG_PTR pages = (ULONG_PTR)100 * 1024 * 1024 / sysInfo.dwPageSize;
    	ULONG_PTR *frameArray = new ULONG_PTR[pages];
    
    	//如果没激活权限,是不能调用这个方法的,可以调用,但是返回FALSE
    	BOOL flag = AllocateUserPhysicalPages(GetCurrentProcess(), &pages, frameArray);
    
    	if (flag == false)
    	{
    		cout << "=========物理内存分配=========" << endl;
    		cout << "分配物理内存失败!" << endl;
    		cout << endl << endl;
    		system("pause");
    	}
    	else
    	{
    		MEMORYSTATUS memStatusVirtual4;
    		GlobalMemoryStatus(&memStatusVirtual4);
    
    		cout << "=========分配物理内存=========" << endl;
    		cout << "减少物理内存=" << memStatusVirtual3.dwAvailPhys - memStatusVirtual4.dwAvailPhys << endl;
    		cout << "减少可用页文件=" << memStatusVirtual3.dwAvailPageFile - memStatusVirtual4.dwAvailPageFile << endl;
    		cout << "减少可用进程空间=" << memStatusVirtual3.dwAvailVirtual - memStatusVirtual4.dwAvailVirtual << endl << endl;
    
    		MEMORYSTATUS memStatusVirtual5;
    		GlobalMemoryStatus(&memStatusVirtual5);
    		cout << "=========释放物理内存=========" << endl;
    		cout << "增加物理内存=" << memStatusVirtual5.dwAvailPhys - memStatusVirtual4.dwAvailPhys << endl;
    		
    	}
    }
    
    int main()
    {
    	D_F();	
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述
    在这里插入图片描述
    结果分析:
    本实验采取了先对虚拟内存进行分配和释放,再对物理内存进行分配和释放的方式,通过Windows系统提供的函数和数据结构显示观察了虚拟内存空间和物理内存空间的变化,显示结果如以上两图所示。通过实验结果分析,具体如下:
    (1)对于虚拟内存来说,分配时有时会产生页目和页表放在物理内存里,相应地物理内存会减小(减少的物理内存比较小),释放分配的虚拟内存时,增加的虚拟内存空间大小=减少的虚拟内存空间大小,符合预期结果。
    (2)对于物理内存来说,用户行为往往不能改变物理内存的分配和释放,在本实验中,对激活物理内存中的锁定页进行了尝试,虽然能打开进程的权限句柄并获取到内存锁定页的权限,但是却不能使用内存锁定页的权限,所以最终无法分配物理内存。
    (3)如果想要进一步解决不能使用内存中锁定页权限的问题,可以通过查阅资料,检查本地权限,然后对其尝试不同的方式进行修改。
    (终于可以把自己说服了)

    小小总结一下

    (1)malloc和new在windows系统中分配的都是虚拟内存。
    (2)如果想要分配物理内存,需要调用allocateuserphysicalpages命令。(但是,使用这个API之前,需要激活内存中锁定页权限,因为用户行为是不能对物理内存进行修改的)

    结语

    如果你喜欢我写的文章,欢迎来踩我个人搭建的博客~
    ChengNing’s Blog

    展开全文
  • Windows虚拟内存简介

    千次阅读 2017-09-30 15:13:00
    Windows系统中,系统内存本身的空间可能非常有限,但是通过虚拟内存(Virtual Memory),可以让程序可以拥有超过系统物理内存大小的可用内存空间。   顾名思义,虚拟内存是逻辑层面的划分。操作系统通过内存...

    Windows系统中,系统内存本身的空间可能非常有限,但是通过虚拟内存(Virtual Memory),可以让程序可以拥有超过系统物理内存大小的可用内存空间。

     

    顾名思义,虚拟内存是逻辑层面的划分。操作系统通过内存管理器(Memory Manager)将虚拟内存地址与物理地址进行对应。通常在系统中,虚拟内存的大小都要大于系统的实际内存大小,因此内存管理器会将一部分虚拟内存中的内容映射到磁盘中。

     

    当应用访问虚拟内存地址的时候,如果内存管理器发现对应的物理地址在磁盘中时,内存管理器会将这部分信息从磁盘中加载回内存中以供应用程序访问。

     

    对于32位操作系统来说,地址总线的宽度也是32位,因此内存寻址的最大寻址空间数量为232次方,即4G个地址空间,每个地址空间对应1个字节的存储单元,所以理论上32位的系统支持的虚拟内存大小最大为4GB。在这4G个虚拟内存地址中,默认情况下,Windows会将低位地址(0x00000000-0x7FFFFFFF)的这一半分配给用户进程进行使用,而将高位地址(0x80000000-0xFFFFFFFF)这一半分配给操作系统进程来使用。这个比例可以在计算机启动设置中修改,最多可以为用户进程分配3G个虚拟内存地址。

     

    Windows内存分页的机制:

    Windows内存地址转换主要通过一个称为页表目录的表来进行映射,在Intel x86 CPU中,页表目录的入口指针存储在寄存器CR3中,指针指向一个长度为1024×32位大小的数组(每32位对应一个虚拟地址),也就是下图中的页目录:

    物 理 内 CR3 寄 存 器 指 向 页 目 录 首 地 址 磁 盘 存 储 器 页 表 页 目 录

    在页目录中每个虚拟地址的组成:

    22 《 21 12 《 11 《 页 目 求 萦 引 0 个 可 能 值 凵 页 表 萦 引 0 个 可 能 值 凵 页 中 的 位 置 ( “ 96 个 可 能 值 凵

    其中前10位用于定义页目录的索引(210次方恰好为1024),中间10位对应这一页对应的物理内存的首地址,最后12位对应数据相对于首地址的偏移量,所以上图中一个页中可以对应4096个地址,即一个页的大小为4KB

     

    对于很多内存使用量很大的应用来说,2GB的可用内存空间远远不能满足需要,因此Windows提供了地址窗口扩展(AWEAddress Windowing Extension)的接口来满足更大内存分配需求的应用程序,通过AWE,应用程序可以在自己的地址空间中保存一个视图(View)或者窗口(Window),然后将物理内存与这个视图或窗口进行映射,对2GB的用户进程可占用的内存来说,最多可以为其映射64GB的物理内存。虽然AWE为程序开发者增加了负担,同时也在映射上增加了开销,但是确实为32位系统下的很多需要大内存的应用程序提供了一个可用方案。为了提高映射的效率以及增加硬件层面的支持,AWE规定了很多限制,具体可以参考https://msdn.microsoft.com/en-us/library/windows/desktop/aa366527(v=vs.85).aspx

     

    相比而言,64bit的操作系统提供了更大的寻址空间,理论上64位总线应该支持264次方个地址空间,即16EB1EB=2^60B)内存,但是由于实际物理架构的限制,当前对于IA-64 CPU来说,最多可以支持7TB的虚拟内存大小,而x64可支持8TB的虚拟内存,Windows系统当前将这个值限制为8TB

     

    虚拟内存的大小可以在操作系统下面的位置进行修改:

    在计算机属性中找到高级设置(Advancedsystem settings),在高级选项卡中找到性能的设置,在打开的窗口中找到高级选项卡,下边就是虚拟内存的设置了:

    Control Panel All Control Panel Items System Properties Control Panel Home Computer Name H System em Protection Remote Performance Options Advanced ssor sch uling ta Execution Prevention Device Manager You must be Remote settings ormance System protection on as an Administrato make most of these changes Visual effects. processor scheduling memory usag nd virtual memory Advanced system settings Desktop settings related to your sign-in Startup and Recovery System startup system failure. and debugging information Environment Variables . See also Security and Maintenance Choose hon to ocate processor resources. Adjust for best per C) Backgr ound services Virtual memory A paging fie is an area on the hard disk that Windows uses as if it vvere RAM. Total paging file size for all drives: 8192 ME

     

    点击修改,调整虚拟内存的值:

    计算机生成了可选文字:Virtual Memory Automatcally manage paging file size for all drives aging e size Drive C'/olume Label) Selected drive : @Custom size. Inital size (MB): Maximum size (ME): p File Size (Ma) S stem mana ed 20480 System managed size C) No paging file Total paging file size for al drives Minimum allowed. Currently allocated: 16 ME 2901 ME 8192 ME

     

    参考链接:

    https://bbs.pediy.com/thread-135274.htm

    http://blog.csdn.net/lanuage/article/details/52003700

    http://book.51cto.com/art/200910/159944.htm

    http://blog.csdn.net/tttt418/article/details/6113860

    http://www.cnblogs.com/VIPler/p/4282584.html

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa366527(v=vs.85).aspx

    展开全文
  • 页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一、基本概念1. 两个内存概念物理内存:人尽皆知,就是插在主板上的内存条。他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外...

    本文主要内容:
    1.基本概念:物理内存、虚拟内存;物理地址、虚拟地址、逻辑地址;页目录,页表
    2.Windows内存管理
    3.CPU段式内存管理
    4.CPU页式内存管理
     
    一、基本概念
    1. 两个内存概念
    物理内存:人尽皆知,就是插在主板上的内存条。他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外)。但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存占用,甚至导致物理内存消耗殆尽。
    虚拟内存:简明的说,虚拟内存就是在硬盘上划分一块页面文件,充当内存。当程序在运行时,有一部分资源还没有用上或者同时打开几个程序却只操作其中一个程序时,系统没必要将程序所有的资源都塞在物理内存中,于是,系统将这些暂时不用的资源放在虚拟内存上,等到需要时在调出来用。
    2.三个地址概念
    物理地址(physical address):用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。
    ——这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到 最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。所以,说它是“与 地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。
    逻辑地址(logical address):是指由程序产生的与段相关的偏移地址部分。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换);逻辑也就是在Intel 保护模式下程序执行代码段限长内的偏移地址(假定代码段、数据段如果完全一样)。应用程序员仅需与逻辑地址打交道,而分段和分页机制对您来说是完全透明的,仅由系统编程人员涉及。应用程序员虽然自己可以直接操作内存,那也只能在操作系统给你分配的内存段操作。
    Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上例,我们说的连接器为A分配的0x08111111这个地址就是逻辑地址。
    ——不过不好意思,这样说,好像又违背了Intel中段式管理中,对逻辑地址要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量, 表示为 [段标识符:段内偏移量],也就是说,上例中那个0x08111111,应该表示为[A的代码段标识符: 0x08111111],这样,才完整一些”
    线性地址(linear address)或也叫虚拟地址(virtual address)
    跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。
    -------------------------------------------------------------
    每个进程都有4GB的虚拟地址空间
    这4GB分3部分 
    (1)一部分映射物理内存
    (2)一部分映射硬盘上的交换文件
    (3)一部分什么也不做
    程序中都是使用4GB的虚拟地址,访问物理内存需要使用物理地址,物理地址是放在寻址总线上的地址,以字节(8位)为单位。
    -------------------------------------------------------------
    CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!!!),CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。
    这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel完全是为了兼容而已。
    3.页表、页目录概念
    使用了分页机制之后,4G的地址空间被分成了固定大小的页,每一页或者被映射到物理内存,或者被映射到硬盘上的交换文件中,或者没有映射任何东西。对于一般程序来说,4G的地址空间,只有一小部分映射了物理内存,大片大片的部分是没有映射任何东西。物理内存也被分页,来映射地址空间。对于32bit的 Win2k,页的大小是4K字节。CPU用来把虚拟地址转换成物理地址的信息存放在叫做页目录和页表的结构里。
    物理内存分页,一个物理页的大小为4K字节,第0个物理页从物理地址 0x00000000 处开始。由于页的大小为4KB,就是0x1000字节,所以第1页从物理地址 0x00001000处开始。第2页从物理地址0x00002000处开始。可以看到由于页的大小是4KB,所以只需要32bit的地址中高20bit来寻址物理页。
    页目录:  一个页目录大小为4K字节,放在一个物理页中。由1024个4字节的页目录项组成。页目录项的大小为4 个字节(32bit),所以一个页目录中有1024个页目录项。页目录中的每一项的内容(每项4个字节)高20bit用来放一个页表(页表放在一个物理页中)的物理地址,低12bit放着一些标志。
    页表:  一个页表的大小为4K字节,放在一个物理页中。由1024个4字节的页表项组成。页表项的大小为4个字节 (32bit),所以一个页表中有1024个页表项。页表中的每一项的内容(每项4个字节,32bit)高20bit用来放一个物理页的物理地址,低 12bit放着一些标志。
    对于x86系统,页目录的物理地址放在CPU的CR3寄存器中。
    4. 虚拟地址转换成物理地址
    一个虚拟地址大小为4字节,其中包含找到物理地址的信息
    虚拟地址分3部分
    (1)31-22位(10位)是页目录中的索引
    (2)21-12位(10位)是页表中的索引
    (2)11-0位(12位)是页内偏移

    转换过程:
    首先通过CR3找到页目录所在物理页-》根据虚拟地址中的31-22找到该页目录项-》通过该页目录项找到该虚拟地址对应的页表地址-》根据虚拟地址21-12找到物理页的物理地址-》更具虚拟地址的11-0位作为偏移加上该物理页的地址就找到了 该虚拟地址对应的物理地址
    CPU把虚拟地址转换成物理地址:一个虚拟地址,大小4个字节(32bit),包含着找到物理地址的信息,分为3个部分:第22位到第31位这10位(最高10位)是页目录中的索引,第 12位到第21位这10位是页表中的索引,第0位到第11位这12位(低12位)是页内偏移。对于一个要转换成物理地址的虚拟地址,CPU首先根据CR3 中的值,找到页目录所在的物理页。然后根据虚拟地址的第22位到第31位这10位(最高的10bit)的值作为索引,找到相应的页目录项(PDE, page directory entry),页目录项中有这个虚拟地址所对应页表的物理地址。有了页表的物理地址,根据虚拟地址的第12位到第21位这10位的值作为索引,找到该页表中相应的页表项(PTE,page table entry),页表项中就有这个虚拟地址所对应物理页的物理地址。最后用虚拟地址的最低12位,也就是页内偏移,加上这个物理页的物理地址,就得到了该虚拟地址所对应的物理地址。
    -------------------------------------------------------------
    一个页目录有1024项,虚拟地址最高的10bit刚好可以索引1024项(2的10次方等于1024)。一个页表也有1024项,虚拟地址中间部分的 10bit,刚好索引1024项。虚拟地址最低的12bit(2的12次方等于4096),作为页内偏移,刚好可以索引4KB,也就是一个物理页中的每个字节。
    -------------------------------------------------------------
    一个虚拟地址转换成物理地址的计算过程就是,处理器通过CR3找到当前页目录所在物理页,取虚拟地址的高10bit,然后把这10bit右移2bit(因为每个页目录项4个字节长,右移2bit相当于乘4)得到在该页中的地址,取出该地址处PDE(4个字节),就找到了该虚拟地址对应页表所在物理页,取虚拟地址第12位到第21位这10位,然后把这10bit右移2bit(因为每个页表项4个字节长,右移2bit相当于乘4)得到在该页中的地址,取出该地址处的PTE(4个字节),就找到了该虚拟地址对应物理页的地址,最后加上12bit的页内偏移得到了物理地址。
    -------------------------------------------------------------
    32bit的一个指针,可以寻址范围0x00000000-0xFFFFFFFF,4GB大小。也就是说一个32bit的指针可以寻址整个4GB地址空间的每一个字节。一个页表项负责4K的地址空间和物理内存的映射,一个页表1024项,也就是负责1024*4k=4M的地址空间的映射。一个页目录项,对应一个页表。一个页目录有1024项,也就对应着1024个页表,每个页表负责4M地址空间的映射。1024个页表负责1024*4M=4G的地址空间映射。一个进程有一个页目录。所以以页为单位,页目录和页表可以保证4G的地址空间中的每页和物理内存的映射。
    -------------------------------------------------------------
    每个进程都有自己的4G地址空间,从0x00000000-0xFFFFFFFF。通过每个进程自己的一套页目录和页表来实现。由于每个进程有自己的页目录和页表,所以每个进程的地址空间映射的物理内存是不一样的。两个进程的同一个虚拟地址处(如果都有物理内存映射)的值一般是不同的,因为他们往往对应不同的物理页。

    4G地址空间中低2G,0x00000000-0x7FFFFFFF是用户地址空间,4G地址空间中高2G,即0x80000000-0xFFFFFFFF 是系统地址空间。访问系统地址空间需要程序有ring0的权限。
     
    二. windows内存原理
     
    主要的内容如下:
    1.概述
    2.虚拟内存
    3.物理内存
    4.映射

    1.概述:
    windows中 我们一般编程时接触的都是线性地址 也就是我们所说的虚拟地址,然而很不幸在我不断成长的过程中发现原来线性地址是操作系统自己意淫出来的,根本就不是我们数据真实存在的地方.换句话说我们在0x80000000(虚拟地址)的地方写入了"UESTC"这个字符串,但是我们这个字符串并不真实存在于物理地址的0x80000000这里.再说了真实的物理地址是利用一段N长的数组来定位的(额~看不懂这句话没关系,一会看到物理地址那你就明白了).但是为什么windows乃至linux都需要采取这种方式来寻址呢?原因很简单 为了安全.听说过保护模式吧?顾名思义就是这个模式下加入了保护系统安全的措施,同样采用线性地址也是所谓的安全措施之一.
        我们假设下如果没有使用线性地址,那么我们可以直接访问物理地址,但是这样的话当我们往内存写东西的时候操作系统无法检查这块内存是否可写,换句话说操作系统无法实现对页面访问控制.这点是很可怕的事情,就如win9x那样没事在应用态往内核地址写东西,还有没有天理了~~
        由于操作系统的安全需要,催生了虚拟地址的应用.在CPU中有个叫MMU(应该是Memory Manage Unit 内存管理单元)的东西,专门负责线性地址和物理地址之间的转化.我们每次读写内存,从CPU的结构看来不是都要经过ALU么,ALU拿到虚拟地址后扔给MMU转化成物理地址后再把数据读入寄存器中.过程就是如此.

    2.虚拟内存
        我们编程时所面对的都是虚拟地址,对于每个进程来说都拥有4G的虚拟内存(小补充: 4G虚拟内存中,高2G内存属于内核部分,是所有进程共有的,低2G内存数据是进程独有的,每个进程低2G内存都不一样),但注意的是虚拟地址是操作系统自己意淫出来的,打个比方就是说想法还未付诸实践,所以不构成任何资源损失.比如我们要在0x80000000的地方写个"UESTC"的时候,操作系统就会将这个虚拟地址映射到一块物理地址A中,你写这块虚拟地址就相当于写物理地址A.但是加入我们只申请了一段1KB的虚拟内存空间,并未读写,系统是不会分配任何物理内存的,只有当虚拟内存要使用时系统才会分配相应的物理空间.
        下面要说些细节点的了,其实说白了虚拟内存的管理是由一堆数据结构来实现的,下面给出这些数据结构:
    (懒得打那么多 就只打出重要的部分~~)
    在EPROCESS中有个数据结构如下:
    typedef struct _MADDRESS_SPACE
    {
        PMEMORY_AREA MemoryAreaRoot ; //这个指针指向一颗二叉排序树,想必学过数据结构的朋友都晓得吧~~嘿嘿~~ 主要是这个情况下采用二叉排序树能加快内存的搜索速度 
        ...
        ...
        ...
    }MADDRESS_SPACE , *PMADDRESS_SPACE ;

    然而这颗二叉排序树的节点结构结构是这样的:
    typedef struct _MEMORY_AREA
    {
        PVOID StartingAddress ; //虚拟内存段的起始地址
        PVOID EndingAddress ;   //虚拟内存段的结束地址
        struct _MEMORY_AREA *Parent ; //该节点的老爹
        struct _MEMORY_AREA *LeftChild ; //该节点的左儿子
        struct _MEMORY_AREA *RrightChild ; //该节点的左儿子
        ...
        ...
        ...

    }MEMORY_AREA , *PMEMORY_AREA ;
        这个节点内主要记录了已分配的虚拟内存空间,如果要申请虚拟内存空间就跑来这里创建个节点就好了,如果要删除空间同样把对应节点删除就好了.不过说来简单,其实还会涉及到很多操作,比如要平衡这棵树什么的.
        那么我们在分配虚拟内存空间时系统会跑去找到这颗树,然后通过一定算法遍历这颗树,找到符合条件的内存空隙(未分配的内存空间),创建个节点挂到这颗树上,返回起始地址就完成了.


    3.物理内存
        接下来就到物理内存的东东了,其实呢 物理内存的管理是基于一个数组来管理的,听说过分页机制吧,下面说下分页.windows下分页是4kb一页 那么假设我们物理内存有4GB 那么windows会将这4GB空间分页,分成4GB/4KB = 1M页    那么每一页(就是4KB)的物理空间都由一个叫PHYSICAL_PAGE的数据结构管理,这个数据结构就不写啦....一来我写的手酸 二来看的人也累~~说说思路就好了.
        接着以上假设 4GB的内存 操作系统就会相应产生一个PHYSICAL_PAGE数组,这个数组有多少个元素呢?有1M个 正好覆盖了4GB的物理地址.这就是所谓的分页机制的原型.说白了就是把内存按4k划分来管理.那么物理地址就好定位了,所谓的物理内存地址就可以直接以数组下标来表示,其实这里的物理内存地址指的是物理内存地址的页面号... 具体地址还是要根据虚拟内存地址的低12位和物理内存的页面号一起确定的 
         再说下物理内存的管理吧,在内核下有3个队列 这些队列内的元素就是上边所说的PHYSICAL_PAGE结构 
    它们分别是:
    A.已分配的内存队列 :存放正被使用的内存
    B.待清理的内存队列 :存放已被释放的内存,但是这些内存未被清理(清零)
    C.空闲队列 :存放可使用的空闲内存

    系统管理流程如下:
    1).每隔一段时间,系统会自动从B队列中提取队列元素进行清理,然后放入空闲队列中.
    2).每当释放物理内存时,系统会将要释放的内存从A队列提取出来,放入B队列中.
    3).申请内存时,系统将要分配的内存从C队列中提取出来,放入A队列中

    4.映射 
        说到映射得先从虚拟内存的32位地址说起.在CPU中存在个CR3寄存器,里面存放着每个进程的页目录地址

    我们可以把转换过程分成几步看
    1.根据CR3(注:CR3中的值是物理地址)的值我们可以定位到当前进程的页目录基址,然后通过虚拟地址的高10位做偏移量来获得指定的PDE(Page Directory Entry),PDE内容有4字节,高20位部分用来做页表基址,剩下的比特位用来实现权限控制之类的东西了.系统只要检测相应的比特位就可以实现内存的权限控制了.
    2.通过PDE提供的基址加上虚拟内存的中10位(21-12)做偏移量就找到了页表PTE(Page Table Entry)地址,然后PTE的高20位就是物理内存的基址了(其实就是那个PHYSICAL_PAGE数组的下标号....),剩下的比特位同样用于访问控制之类的.
    3.通过虚拟内存的低12位加上PTE中高20位做基址就可以确定唯一的物理内存了.

       
    三. CPU段式内存管理,逻辑地址如何转换为线性地址
    一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节,如图:

    Windows内存管理 - 啸百川 - 啸百川的博客

    最后两位涉及权限检查,本贴中不包含。

    索引号,或者直接理解成数组下标——那它总要对应一个数组吧,它又是什么东东的索引呢?这个东东就是“段描述符(segment descriptor)”,呵呵,段描述符具体地址描述了一个段(对于“段”这个字眼的理解,我是把它想像成,拿了一把刀,把虚拟内存,砍成若干的截—— 段)。这样,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描 述符就描述了一个段,我刚才对段的抽像不太准确,因为看看描述符里面究竟有什么东东——也就是它究竟是如何描述的,就理解段究竟有什么东东了,每一个段描 述符由8个字节组成,如下图:

    Windows内存管理 - 啸百川 - 啸百川的博客

    这些东东很复杂,虽然可以利用一个数据结构来定义它,不过,我这里只关心一样,就是Base字段,它描述了一个段的开始位置的线性地址。

    Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表 (LDT)”中。那究竟什么时候该用GDT,什么时候该用LDT呢?这是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。

    GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。

    好多概念,像绕口令一样。这张图看起来要直观些:

    Windows内存管理 - 啸百川 - 啸百川的博客

    首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],
    1、看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。
    2、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。
    3、把Base + offset,就是要转换的线性地址了。

    还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。OK,来看看Linux怎么做的。

    Linux的段式管理
    Intel要求两次转换,这样虽说是兼容了,但是却是很冗余,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一样。
    另一方面,其它某些硬件平台,没有二次转换的概念,Linux也需要提供一个高层抽像,来提供一个统一的界面。所以,Linux的段式管理,事实上只是“哄骗”了一下硬件而已。

    按照Intel的本意,全局的用GDT,每个进程自己的用LDT——不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用 户代码段,对应的,内核中的是内核数据段和内核代码段。这样做没有什么奇怪的,本来就是走形式嘛,像我们写年终总结一样。
    [Copy to clipboard] [ - ]
    CODE:
    #define GDT_ENTRY_DEFAULT_USER_CS 14
    #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)

    #define GDT_ENTRY_DEFAULT_USER_DS 15
    #define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)

    #define GDT_ENTRY_KERNEL_BASE 12

    #define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0)
    #define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)

    #define GDT_ENTRY_KERNEL_DS (GDT_ENTRY_KERNEL_BASE + 1)
    #define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)
    把其中的宏替换成数值,则为:
    [Copy to clipboard] [ - ]
    CODE:
    #define __USER_CS 115        [00000000 1110   0   11]
    #define __USER_DS 123        [00000000 1111   0   11]
    #define __KERNEL_CS 96    [00000000 1100   0   00]
    #define __KERNEL_DS 104 [00000000 1101   0   00]
    方括号后是这四个段选择符的16位二制表示,它们的索引号和T1字段值也可以算出来了
    [Copy to clipboard] [ - ]
    CODE:
    __USER_CS              index= 14 T1=0
    __USER_DS             index= 15 T1=0
    __KERNEL_CS           index=   12   T1=0
    __KERNEL_DS           index= 13 T1=0
    T1均为0,则表示都使用了GDT,再来看初始化GDT的内容中相应的12-15项(arch/i386/head.S):
    [Copy to clipboard] [ - ]
    CODE:
    .quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */
    .quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */
    .quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */
    .quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */

    按照前面段描述符表中的描述,可以把它们展开,发现其16-31位全为0,即四个段的基地址全为0。
    这样,给定一个段内偏移地址,按照前面转换公式,0 + 段内偏移,转换为线性地址,可以得出重要的结论,“在Linux下,逻辑地址与线性地址总是一致(是一致,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。!!!”
    忽略了太多的细节,例如段的权限检查。呵呵。
    Linux中,绝大部份进程并不例用LDT,除非使用Wine ,仿真Windows程序的时候。

    四.CPU页式内存管理

    CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页 (page),例如一个32位的机器,线性地址最大可为4G,可以用4KB为一个页来划分,这页,整个线性地址就被划分为一个tatol_page [2^20]的大数组,共有2的20个次方个页。这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址——对应的页的地址。
    另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。
    这里注意到,这个total_page数组有2^20个成员,每个成员是一个地址(32位机,一个地址也就是4字节),那么要单单要表示这么一个数组,就要占去4MB的内存空间。为了节省空间,引入了一个二级管理模式的机器来组织分页单元。文字描述太累,看图直观一些:

    Windows内存管理 - 啸百川 - 啸百川的博客

    如上图,
    1、分页单元中,页目录是唯一的,它的地址放在CPU的cr3寄存器中,是进行地址转换的开始点。万里长征就从此长始了。
    2、每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。——运行一个进程,需要将它的页目录地址放到cr3寄存器中,将别个的保存下来。
    3、每一个32位的线性地址被划分为三部份,面目录索引(10位):页表索引(10位):偏移(12位)
    依据以下步骤进行转换:
    1、从cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);
    2、根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
    3、根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;
    4、将页的起始地址与线性地址中最后12位相加,得到最终我们想要的葫芦;

    这个转换过程,应该说还是非常简单地。全部由硬件完成,虽然多了一道手续,但是节约了大量的内存,还是值得的。那么再简单地验证一下:
    1、这样的二级模式是否仍能够表示4G的地址;
    页目录共有:2^10项,也就是说有这么多个页表
    每个目表对应了:2^10页;
    每个页中可寻址:2^12个字节。
    还是2^32 = 4GB

    2、这样的二级模式是否真的节约了空间;
    也就是算一下页目录项和页表项共占空间 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么说呢!!!(真的减少了吗?因该是增加了吧,(4 + 2^10 * 4 + 2 ^10 * 2 ^10 *4) = 4100KB+4Byte)

    值得一提的是,虽然页目录和页表中的项,都是4个字节,32位,但是它们都只用高20位,低12位屏蔽为0——把页表的低12屏蔽为0,是很好理解的,因 为这样,它刚好和一个页面大小对应起来,大家都成整数增加。计算起来就方便多了。但是,为什么同时也要把页目录低12位屏蔽掉呢?因为按同样的道理,只要 屏蔽其低10位就可以了,不过我想,因为12>10,这样,可以让页目录和页表使用相同的数据结构,方便。

    本贴只介绍一般性转换的原理,扩展分页、页的保护机制、PAE模式的分页这些麻烦点的东东就不啰嗦了……可以参考其它专业书籍。
     
     
     
     Win32通过一个两层的表结构来实现地址映射,因为每个进程都拥有私有的4G的虚拟内存空间,相应的,每个进程都有自己的层次表结构来实现其地址映射。
          第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页以4个字节分为1024项,每一项称为“页目录项”(PDE);
          第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页以4KB的大小被分为1024项,页表的每一项被称为页表项(PTE),易知共有1024×1024个页表项。每一个页表项对应一个物理内存中的某一个“内存页”,即共有1024×1024个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。
    如下图所示(注下图中的页目录项和页表项的大小应该是4个字节,而不是4kB):

          Win32提供了4GB大小的虚拟地址空间。因此每个虚拟地址都是一个32位的整数值,也就是我们平时所说的指针,即指针的大小为4B。它由三部分组成,如下图:

          这三个部分的第一部分,即前10位为页目录下标,用来寻址页目录项,页目录项刚好1024个。找到页目录项后,找对页目录项对应的的页表。第二部分则是用来在页表内寻址,用来找到页表项,共有1024个页表项,通过页表项找到物理内存页。第三部分用来在物理内存页中找到对应的字节,一个页的大小是4KB,12位刚好可以满足寻址要求。
    具体的例子:
    假设一个线程正在访问一个指针(Win32的指针指的就是虚拟地址)指向的数据,此指针指为0x2A8E317F,下图表示了这一个过程:

    0x2A8E317F的二进制写法为0010101010_0011100011_000101111111,为了方便我们把它分为三个部分。
    首先按照0010101010寻址,找到页目录项。因为一个页目录项为4KB,那么先将0010101010左移两位,001010101000(0x2A8),用此下标找到页目录项,然后根据此页目录项定位到下一层的某个页表。
    然后按照0011100011寻址,在上一步找到页表中寻找页表项。寻址方法与上述方法类似。找到页表项后,就可以找到对应的物理内存页。
    最后按照000101111111寻址,寻找页内偏移。
          上面的假设的是此数据已在物理内存中,其实判断访问的数据是否在内存中也是在地址映射过程中完成的。Win32系统总是假设数据已在物理内存中,并进行地址映射。页表项中有一位标志位,用来标识包含此数据的页是否在物理内存中,如果在的话,就直接做地址映射,否则,抛出缺页中断,此时页表项也可标识包含此数据的页是否在调页文件中(外存),如果不在则访问违例,程序将会退出,如果在,页表项会查出此数据页在哪个调页文件中,然后将此数据页调入物理内存,再继续进行地址映射。为了实现每个进程拥有私有4G的虚拟地址空间,也就是说每个进程都拥有自己的页目录和页表结构,对不同进程而言,即使是相同的指针(虚拟地址)被不同的进程映射到的物理地址也是不同的,这也意味着在进程之间传递指针是没有意义的。


    Linux的页式内存管理
    原理上来讲,Linux只需要为每个进程分配好所需数据结构,放到内存中,然后在调度进程的时候,切换寄存器cr3,剩下的就交给硬件来完成了(呵呵,事实上要复杂得多,不过偶只分析最基本的流程)。

    前面说了i386的二级页管理架构,不过有些CPU,还有三级,甚至四级架构,Linux为了在更高层次提供抽像,为每个CPU提供统一的界面。提供了一个四层页管理架构,来兼容这些二级、三级、四级管理架构的CPU。这四级分别为:

    页全局目录PGD(对应刚才的页目录)
    页上级目录PUD(新引进的)
    页中间目录PMD(也就新引进的)
    页表PT(对应刚才的页表)。

    整个转换依据硬件转换原理,只是多了二次数组的索引罢了,如下图:

    Windows内存管理 - 啸百川 - 啸百川的博客

    那么,对于使用二级管理架构32位的硬件,现在又是四级转换了,它们怎么能够协调地工作起来呢?嗯,来看这种情况下,怎么来划分线性地址吧!
    从硬件的角度,32位地址被分成了三部份——也就是说,不管理软件怎么做,最终落实到硬件,也只认识这三位老大。
    从软件的角度,由于多引入了两部份,,也就是说,共有五部份。——要让二层架构的硬件认识五部份也很容易,在地址划分的时候,将页上级目录和页中间目录的长度设置为0就可以了。
    这样,操作系统见到的是五部份,硬件还是按它死板的三部份划分,也不会出错,也就是说大家共建了和谐计算机系统。

    这样,虽说是多此一举,但是考虑到64位地址,使用四层转换架构的CPU,我们就不再把中间两个设为0了,这样,软件与硬件再次和谐——抽像就是强大呀!!!

    例如,一个逻辑地址已经被转换成了线性地址,0x08147258,换成二制进,也就是:
    0000100000 0101000111 001001011000
    内核对这个地址进行划分
    PGD = 0000100000
    PUD = 0
    PMD = 0
    PT = 0101000111
    offset = 001001011000

    现在来理解Linux针对硬件的花招,因为硬件根本看不到所谓PUD,PMD,所以,本质上要求PGD索引,直接就对应了PT的地址。而不是再到PUD和 PMD中去查数组(虽然它们两个在线性地址中,长度为0,2^0 =1,也就是说,它们都是有一个数组元素的数组),那么,内核如何合理安排地址呢?
    从软件的角度上来讲,因为它的项只有一个,32位,刚好可以存放与PGD中长度一样的地址指针。那么所谓先到PUD,到到PMD中做映射转换,就变成了保 持原值不变,一一转手就可以了。这样,就实现了“逻辑上指向一个PUD,再指向一个PDM,但在物理上是直接指向相应的PT的这个抽像,因为硬件根本不知 道有PUD、PMD这个东西”。

    然后交给硬件,硬件对这个地址进行划分,看到的是:
    页目录 = 0000100000
    PT = 0101000111
    offset = 001001011000
    嗯,先根据0000100000(32),在页目录数组中索引,找到其元素中的地址,取其高20位,找到页表的地址,页表的地址是由内核动态分配的,接着,再加一个offset,就是最终的物理地址了。
     
     
    五. 存储方式
    保护模式现代操作系统的基础,理解他是我们要翻越的第一座山。保护模式是相对实模式而言的,他们是处理器的两种工作方式。很久以前大家使用的dos就是运行在实模式下,而现在的windows操作系统则是运行在保护模式下。两种运行模式有着较大的不同,
    实模式由于是由8086/8088发展而来因此他更像是一个运行单片机的简单模式,计算机启动后首先进入的就是实模式,通过8086/8088只有20根地址线所以它的寻址范围只有2的20次幂,即1M。内存的访问方式就是我们熟悉的seg:offset逻辑地址方式,例如我们给出地址逻辑地址它将在cpu内转换为20的物理地址,即将seg左移4位再加上offset值。例如地址1000h:5678h,则物理地址为10000h+5678h=15678h。实模式在后续的cpu中被保留了下来,但实模式的局限性是很明显的,由于使用seg:offset逻辑地址只能访问1M多一点的内存空间,在拥有32根地址线的cpu中访问1M以上的空间则变得很困难。而且随着计算机的不断发展实模式的工作方式越来越不能满足计算机对资源(存储资源和cpu资源等等)的管理,由此产生了新的管理方式——保护模式。
    80386及以上的处理器功能要大大超过其先前的处理器,但只有在保护模式下,处理器才能发挥作用。在保护模式下,全部32根地址线有效,可寻址4G的物理地址空间;扩充的存储分段机制和可选的存储器分页机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持;支持多任务;4个特权级和完善的特权级检查机制,实现了数据的安全和保密。计算机启动后首先进入的就是实模式,通过设置相应的寄存器才能进入保护模式(以后介绍)。保护模式是一个整体的工作方式,但分步讨论由浅入深更利于学习。

    存储方式主要体现在内存访问方式上,由于兼容和IA32框架的限制,保护模式在内存访问上延用了实模式下的seg:offset的形式(即:逻辑地址),其实seg:offset的形式在保护模式下只是一个躯壳,内部的存储方式与实模式截然不同。在保护模式下逻辑地址并不是直接转换为物理地址,而是将逻辑地址首先转换为线性地址,再将线性地址转换为物理地址。

    线性地址是个新概念,但大家不要把它想的过于复杂,简单的说他就是0000000h~ffffffffh(即0~4G)的线性结构,是32个bite位能表示的一段连续的地址,但他是一个概念上的地址,是个抽象的地址,并不存在在现实之中。线性地址地址主要是为分页机制而产生的。处理器在得到逻辑地址后首先通过分段机制转换为线性地址,线性地址再通过分页机制转换为物理地址最后读取数据。
     
    分段机制是必须的,分页机制是可选的,当不使用分页的时候线性地址将直接映射为物理地址,设立分页机制的目的主要是为了实现虚拟存储(分页机制在后面介绍)。先来介绍一下分段机制,以下文字是介绍如何由逻辑地址转换为线性地址。
    分段机制在保护模式中是不能被绕过得,回到我们的seg:offset地址结构,在保护模式中seg有个新名字叫做“段选择子”(seg..selector)。段选择子、GDT、LDT构成了保护模式的存储结构,GDT、LDT分别叫做全局描述符表和局部描述符表,描述符表是一个线性表(数组),表中存放的是描述符。
    “描述符”是保护模式中的一个新概念,它是一个8字节的数据结构,它的作用主要是描述一个段(还有其他作用以后再说),用描述表中记录的段基址加上逻辑地址(sel:offset)的offset转换成线性地址。描述符主要包括三部分:段基址(Base)、段限制(Limit)、段属性(Attr)。一个任务会涉及多个段,每个段需要一个描述符来描述,为了便于组织管理,80386及以后处理器把描述符组织成表,即描述符表。在保护模式中存在三种描述符表 “全局描述符表”(GDT)、“局部描述符表”(LDT)和中断描述符表(IDT)(IDT在以后讨论)。
    (1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。

    (2)段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的,如图三①步。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)

    段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符(如图三①步)。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址(如图三②步),段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。例如给出逻辑地址:21h:12345678h转换为线性地址
    a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1
    b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h
    (3)局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图五

    LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。如图五,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。
    1. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR
    2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。OFFSET=12345678h。逻辑地址为1C:12345678h
    3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h
    4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)
    由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。
    存储方式是保护模式的基础,学习他主要注意与实模式下的存储模式的对比,总的思想就是首先通过段选择子在描述符表中找到相应段的描述符,根据描述符中的段基址首先确定段的位置,再通过OFFSET加上段基址计算出线性地址。

    展开全文
  • 虚拟机VMware安装windows10x64完整教程

    万次阅读 多人点赞 2019-09-27 17:29:18
    虚拟机VMware安装windows10x64 我安装Windows虚拟机的原动力,是需要用到Oracle10g。然而很感人的是我的电脑(LX笔记本)安装总是失败,然后完整卸载又要删掉好多注册组件,特别麻烦。最终放弃了这项操作。 下载 ...
  • 内存隔离Windows 10’s April 2018 Update brings “Core Isolation” and “Memory Integrity” security features to everyone. These use virtualization-based security to protect your core operating system ...
  • windows内存泄露检测

    千次阅读 2016-07-22 08:39:56
    对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题。已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等。Smart Pointer技术比较成熟,STL中已经包含支持Smart ...
  • windows内存申请

    千次阅读 2016-09-06 10:43:54
    在进行Windows的学习过程中,经常看到不同的内存分配方式,例如VirtualAlloc, HeapAlloc, malloc和new。它们之间存在一些差异。   (1) VirtualAlloc PVOID VirtualAlloc(PVOID pvAddress, SIZE_T dwSize,...
  • LINUX中swap与windows中虚拟内存区别

    千次阅读 2017-12-11 16:21:56
    很多朋友在安装linux的时候会遇到一个问题,那就是分区。...几乎所有安装linux的教程中都要求划分这个区域,并且其分区大小几乎都按照windows的虚拟内存建议:物理内存的2倍划分。当然了 redhat也有标准建议:
  • windows内存结构概述

    千次阅读 2018-03-24 15:03:22
    13.1 Windows的虚拟地址空间安排 13.1.1虚拟地址空间的分区(即虚拟地址空间布局)   进程的地址空间划分 分区 x86 32位 Windows 3GB用户模式下的x86 32位Windows X64 64位 Windows IA-64 64位 ...
  • Windows10性能优化设置

    千次阅读 2019-07-16 23:10:42
    Windows10性能优化设置 作者:雷神Club 链接:https://www.zhihu.com/question/33855201/answer/603615372 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 刚到手的新电脑,...
  • SylphyHorn是Windows 10的虚拟桌面工具。 提供虚拟机的全局热键,切换通知,更改背景和多项功能。 该应用程序是在.NET Framework上运行的Windows桌面应用程序,但是使用Desktop Bridge构建并在Windows Store中作为...
  • Win10内存管理探索

    千次阅读 2019-12-05 22:30:52
    最近烦恼于电脑内存日常80%+,苦不堪言,明明物理内存有8G,为何那么容易爆满呢?然后就开启了我的内存管理探索之旅~ 1. 任务管理器—内存解析 查看内存状况的第一反应是,任务管理器~ 我们发现一个奇怪的事情,...
  • windows内存结构概述(转)

    千次阅读 2018-09-30 18:29:27
    windows内存结构概述   13.1 Windows的虚拟地址空间安排 13.1.1虚拟地址空间的分区(即虚拟地址空间布局)   进程的地址空间划分 分区 x86 32位 Windows 3GB用户模式下的x86 ...
  • Windows下的修改Tomcat的可用内存

    千次阅读 2018-07-02 11:06:53
    tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,轻微时,会使系统性能急剧下降,严重时,将导致系统无法运行,影响系统的稳定性。当内存不够用时,常见的问题就是报tomcat内存益处错误,...
  • 推动Windows的限制:虚拟内存

    千次阅读 2018-01-31 21:27:03
    在我推出Windows的限制后...推动Windows的限制:物理内存推动Windows的限制:虚拟内存推动Windows的限制:分页和非分页池推动Windows的限制:进程和线程推动Windows的限制:把手推动Windows的限制:USER和GDI对象 -...
  • Windows内存体系(1) -- 虚拟地址空间

    万次阅读 多人点赞 2018-03-19 14:07:17
    一、实模式下内存分配机制 在8086或者80186以前,要运行一个程序,操作系统会把这些程序全都装入内存,程序...例如某台计算机总的内存大小是128M ,现在同时运行两个程序 A和B ,A需占用内存10M , B需占用内存110...
  • windows下logstash7内存及Java设置

    千次阅读 2019-07-31 22:07:11
    windows下logstash7内存及Java设置
  • Windows10系统安装PyTorch完整教程

    千次阅读 2021-02-23 18:38:26
    本文选择cuDNN Library for Windows 10。虽然标题中显示的是X86,即32位架构,但实际下载的文件是X64的。 下载之后,解压缩,将CUDNN压缩包里面的bin、clude、lib文件直接复制到CUDA的安装目录下(C:\Program Files...
  • 面试题总结之windows/linux内存管理

    千次阅读 2017-08-20 23:09:48
    前言请说说你对windows/linux内存管理的认识解答内存管理的必要性出现在早期的计算机系统当中,程序是直接运行在物理内存中,每一个程序都能直接访问物理地址。如果这个系统只运行一个程序的话,并且这个程序所需的...
  • Windows内存管理

    千次阅读 2015-03-12 17:03:33
     2.Windows内存管理  3.CPU段式内存管理  4.CPU页式内存管理   一、基本概念 1. 两个内存概念 物理内存:人尽皆知,就是插在主板上的内存条。他是固定的,内存条的容量多大,物理内存就有多大(集成显卡...
  • 内存映射文件”可以将硬盘上的文件映射到虚拟地址空间,这样就不需要将所有东西都放入到页交换文件中,比如系统有许多程序同时运行时,如果将这些程序文件都加载到页交换文件中,页交换文件将会变得非常大。...
  • 微软实验室 tl; dr 要开始使用MSLab,只需从此存储库的“部分下载最新版本的脚本...Windows 10 Pro / Enterprise(因为需要Hyper-V)或Windows Server 2016/2019 8GB RAM 具有虚拟化支持的CPU 固态硬盘 40GB可用空
  • windows 内存原理与管理

    千次阅读 2017-02-14 17:01:50
    本文基本上是windows via c/c++上的内容,笔记做得不错。。 本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别...对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存
  • Windows提供的API(LoadLibrary, LoadLibraryEx)只支持从文件系统上加载DLL文件,我们无法使用这些API从内存中加载DLL。 但是有些时候,我们的确需要从内存中加载DLL,比如: 对发布的文件数量有限制。我们可以...
  • 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制。 本文目
  • 开始之前,我们不妨先来做一个计算,首先打开此电脑(如果你还在使用wind7,那... C盘文件总大小,并且至少相差10G。那么C盘这10G左右的存储空间究竟去哪了呢?又如何释放着部分空间为我们所用呢?且让我为你细细道来。^ -...
  • pagefile.sys即安装过程中创建的虚拟内存页面文件。这个文件的大小是自己系统虚拟内存设置的最小值的大小。假如虚拟内存的设置为800MB-1600MB 那么你在自己的设定驱动盘下面就可看到大小为800MB的pagefile.sys文件 ...
  • 比如这个个性化设置界面,变化就非常大,我相信经常使用Windows10的小伙伴们一眼都能看出来。 但是对于C盘空间小的朋友们来说,另一个发现可能让大家有些不安,细心的朋友会发现,在C盘根目录下多了一个Wi

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 202,178
精华内容 80,871
关键字:

windows10内存要求