x86的内存x86 物理地址空间映射在多个内存条之间是怎么分配的

内存管理笔记(分页,分段,逻辑地址,物理地址与地址转换方式) - Felix Fang - 博客园
随笔 - 88, 文章 - 63, 评论 - 58, 引用 - 0
本文内容参考自如下博客与书籍:
http://blog.csdn.net/windowseight/article/details/8279863
http://bbs.chinaunix.net/thread--1.html
http://blog.csdn.net/erazy0/article/details/6457626#comments
http://blog.csdn.net/drshenlei/article/details/4261909
http://duartes.org/gustavo/blog/post/memory-translation-and-segmentation
《操作系统概念》第六版中文版,高等教育出版社,第九章,郑扣根译。
《Operating System Concepts》7th Edition, 高等教育出版社。
文中很多处是根据自己理解所写,若有错误,欢迎指出和探讨。
1. 物理地址和逻辑地址
物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查(no translation, no paging, no privilege checks)。
逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干。
为什么会有这两种地址?
个人觉的原因在于逻辑地址分配更加灵活,可以允许不唯一,看起来也较为直观,例如,一段代码中分配数组,逻辑地址上是连续的,然而在物理地址上,这个数组所占用的页可能分散开来,物理地址上就是不连续的,这样对程序的可理解性上有影响。另外,有了逻辑地址这个概念,才能使用虚拟内存技术。
2. Paging,分页内存管理方案
(1)&分页的最大作用就在于:使得进程的物理地址空间可以是非连续的。
物理内存被划分为一小块一小块,每块被称为帧(Frame)。分配内存时,帧是分配时的最小单位,最少也要给一帧。在逻辑内存中,与帧对应的概念就是页(Page)。
逻辑地址的表示方式是:前部分是页码后部分是页偏移。
例如,已知逻辑空间地址为2^m个字节(也就是说逻辑地址的长度是m位),已知页大小是2^n字节。那么一共可以有2^(m-n)个页。因此页码部分会占m-n位,之后的n位,用来存储页偏移。
举个例子,&页大小为4B,而逻辑内存为32B(8页),逻辑地址0的页号为0,页号0对应帧5,因此逻辑地址映射为物理地址5*4+0=20。逻辑地址3映射物理地址5*4+3=23。逻辑地址13(4*3+1,页号为3,偏移为1,因此帧号为2),映射到物理地址9。
采用分页技术不会产生外部碎片(内存都被划分为帧),但可能产生内部碎片(帧已经是最小单元,因此帧内部可能有空间没有用到)。
按概率计算下来,每个进程平均可有半个帧大小的内部碎片。
(2) 页表的硬件实现
上一小节中写到页表是逻辑地址转化到物理地址的关键所在。那么页表如何存储?
每个操作系统都有自己的方法来保存页表。绝大多数都会为每个进程分配一个页表。现在由于页表都比较大,所以放在内存中(以往是放在一组专用寄存器里),其指针存在进程控制块(PCB)里,当进程被调度程序选中投入运行时,系统将其页表指针从进程控制块中取出并送入用户寄存器中。随后可以根据此首地址访问页表。
页表的存储方式是TBL(Translation look-aside buffer, 翻译后备缓冲器)+内存。TBL实际上是一组硬件缓冲所关联的快速内存。若没有TBL,操作系统需要两次内存访问来完成逻辑地址到物理地址的转换,访问页表算一次,在页表中查找算一次。TBL中存储页表中的一小部分条目,条目以键值对方式存储。
(3)&页表的数据结构
今年是2013年,现有的笔记本电脑,内存地址空间一般为2^32字节以上。对于具有32位逻辑地址空间的计算机系统,如果系统的页大小为4KB(2^12B),那么页表可以拥有2^(32-12)个,也就是一百多万个条目,假设每个条目占有4B,那每个进程都需要4MB的物理地址空间来存放页表本身。而且,页表本身需要分配在连续内存中。
为此,Hierarchical Paging(层次化分页)被提出,实际上就是将页号分为两部分,第一部分作为索引,第二部分作为页号的偏移。
以一个4kb页大小的32位系统为例。一个逻辑地址被分为20位的页码和12位的页偏移。因为要对页表进行再分页,所以该页号可分为10位的页码和10位的页偏移。这样一个逻辑地址就表示如下形式:
地址转换过程如下:
地址由外向内转换,因此此方法也被称为forward-mapped page table(向前映射表)。
b. Hashed Page Tables 哈希页表
处理超过32位地址空间的常用方法是使用hashed page table(哈希页表),并以虚拟页码作为哈希值。哈希页表的每一条目都包括一个链表的元素,这些元素哈希成同一位置。每个元素有三个域:虚拟页码,所映射的帧号,指向链表中下一个元素的指针。
个人看来,哈希页表的地址转换方式,实际上是Chaining(链接)方式,也就是一种哈希函数的溢出处理方式(另一种溢出处理方式叫做Open Addressing,开放寻址),具体过程如下:
逻辑地址需要大于32bit的地址空间来表示,但是操作系统仍只有32bit来表示地址。此时人们便想到虚拟页地址,虚拟地址可以在32bit表示范围之内,然后利用哈希函数完成逻辑地址到虚拟地址的映射,由于虚拟地址更少,哈希函数会出现溢出,这里使用Chaining来解决溢出。
逻辑地址中的页号(下图中的p)经过哈希函数的计算,算出虚拟地址中的页号,根据虚拟页号可以在哈希表中以O(1)方式寻址,用p与链表中的每一个元素的第一个域相比较。如果匹配,那么相应的帧号就用来形成物理地址。如果不匹配,就对链表中的下一个节点进行比较,以寻找一个匹配的页号。
c. Inverted page table 反向页表
时间关系,这段暂时略过。
3. Segmentation,分段内存管理方案
采用分页内存管理有一个不可避免的问题:用户视角的内存和实际内存的分离。设想一段main函数代码,里面包含Sqrt函数的调用。按照编写者的理解,这段代码运行时,操作系统应该分配内存给:符号表(编译时使用),栈(存放局部变量与函数参数值),Sqrt代码段,主函数代码段等。这样,编写者就可以方便地指出:"函数sqrt内存模块的第五条指令",来定位一个元素。而实际上,由于采用Paging的管理方式,所有的一切都只是散落在物理内存中的各个帧上,并不是以编写者的理解来划分模块。
Segmentation的内存管理方式可以支持这种思路。逻辑地址空间由一组段组成。每个段都有名字和长度。地址指定了段名称和段内偏移。因此用户通过两个量来指定地址:段名称和偏移。段是编号的,通过段号而非段名称来引用。因此逻辑地址由有序对构成:
&&segment-number,offset&(&段号s, 段内偏移d&)
段偏移d因该在0和段界限之间,如果合法,那么就与基地址相加而得到所需字节在物理内存中的地址。因此段表是一组基地址和界限寄存器对。
例如下图,有5个段,编号0~4,例如段2为400B开始于位置4300,对段2第53字节的引用映射成位置3。而段0字节1222的引用则会触发地址错误,因为该段的仅为1000B长(界限为1000)。
4.&合并分段和分页的管理方案
在现有的Intel兼容计算机(x86)上,采用的内存管理方案是分段和分页合并的管理方案。
在这个方案中,逻辑地址,如前一节中所说,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量]。
这样的逻辑地址转换的过程是怎样呢?如下图所示:
当CPU要执行一条引用了内存地址的指令时,转换过程就开始了。第一步是把逻辑地址转换成线性地址。但是,为什么不跳过这一步,而让软件直接使用线性地址(或物理地址呢?)原因主要是因为:
(1) Intel的更新是渐进式而非革命式,新的处理器需要兼容和保留过往的设置。具体的原因,博文Memory&Translation and Segmentation&() 中讲的较为清楚。
(2) 如上节所说,采用段内存管理,可以跟方便地进行地址保护(同一类型的地址逻辑地址在一起)。
下面讲逻辑地址到线性地址的部分。
在IBM OS/2 32位版本的操作系统,和Intel 386的环境下。操作系统采用的内存分配方式就是分段和分页合并的方式。
逻辑地址的实际上是一对&选择符,偏移&。
选择符的内容如下:
从左开始,13位是索引(或者称为段号),通过这个索引,可以定位到段描述符(segment descriptor),而段描述符是可以真正记载了有关一个段的位置和大小信息, 以及访问控制的状态信息。段描述符一般由8个字节组成。由于8B较大,而Intel为了保持向后兼容,将段寄存器仍然规定为16-bit(尽管每个段寄存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段寄存器就是16-bit的),那么很明显,我们无法通过16-bit长度的段寄存器来直接引用64-bit的段描述符。因此在逻辑地址中,只用13bit记录其索引。而真正的段描述符,被放于数组之中。
这个内存中的数组就叫做GDT(Global&Descriptor&Table,全局描述表),Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址。程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。
除了GDT之外,还有LDT(Local Descriptor&Table,本地描述表),但与GDT不同的是,LDT在系统中可以存在多个,每个进程可以拥有自己的LDT。LDT的内存地址在LDTR寄存器中。
在之前图中的TI位,就是用来表示此索引所指向的段描述符是存于全局描述表中,还是本地描述表中。=0,表示用GDT,=1表示用LDT。
RPL位,占2bit,是保护信息位,还没有仔细了解过这一块,暂时先不写。
找到,段描述符后,加上偏移量,便是线性地址。转换过程如下:
在Intel 386的环境下,线性地址转换为物理地址的过程,和第二节分页式内存管理中,层次分页中,逻辑地址转换为物理地址的方法类似。如下图。
Intel 80386的地址转换全过程如下图:
内存管理部分是操作系统的核心功能之一,这次将理论部分整理出来,一是为了复习,二也是为了提纲挈领地为深入学习操作系统做准备。
文中的图片均非本人原创,主要来自文章开头所引用的博文,以及参考书籍中的图片。若有侵权行为,请指出,博主将尽快移除。X86的内存地址理解
(一)&&&X86体系的内存管理
X86体系的CPU,存在三种类型的地址:
1. 逻辑地址:logical address,逻辑地址由segment +
offset构成。逻辑地址实际上“高层地址”(编程的时候面对的地址),在实际编程的时候,已经被操作系统和编程语言屏蔽了逻辑地址。但是如果使用X86汇编语言时候,需要理解逻辑地址。
2. 线性地址:linear address or virtual
address。CPU通过寄存器以及一定的操作,将逻辑地址翻译成virtual address。
3. 物理地址:physical
address。CPU在实际操作RAM的时候的地址,它和芯片管脚发出的电信号具有匹配关系。对程序员而言,这个地址无意义。但是我们要知道最终CPU真正操作数据的时候是通过这个地址完成的。
CPU体系的内存管理,首先要理解这三种地址的转换关系。这三个地址的转换是由X86体系下的硬件完成的,每个转换都需要有特定的寄存器参与。
(二)&&&逻辑地址到线性地址的转换
逻辑地址到线性地址的转换过程也被称为X86 CPU的“分段机制”。
逻辑地址由Segment +
offset构成。其中Segment存放于CPU的特定寄存器中,有6个段寄存器:CS、SS、DS、ES、FS、GS。段寄存器内存放的内容称为“segment
Selector”。
1. Segment Selector:段选择符,16bits。段选择符的格式如下:
2. Segment register:段寄存器,由CPU提供的寄存器,目的就是专门存放“segment
Selector”。
3. Segment
Descriptor:段描述符,64bits。其中包含32bits的基地址和20bits的段界限(最多一个段定义的地址范围)。一个段描述符的结构如下图所示:
4. Global Descriptor Table(GDT) or Local Descriptor
Tble(LDT):存放段描述符的数组,每个数组存放的值是一个个的Segment
Descriptor。段选择符的index字段为13bits,因此每个数组的最长长度为2^13 * 8 = 8192 * 8 =
65536 Byte,需要占用64K大小的一块内存。
5. GDTR和LDTR:GDT寄存器、LDT寄存器。这两个寄存器存放的就是GDT或者是LDT的首地址。
逻辑地址转换为线性地址的过程: 首先从GDTR寄存器或者LDTR中,获得表的首地址,从segment
register中获得segment Selector,得到Segment
Descriptor在GDT表中的位置,则获得了线性地址的“基址”,加上offset,则就是线性地址。
如果我们自己设计个操作系统,利用X86的分段机制,可以采用这样的设想:系统全局共享的空间可以通过GDT来安排和访问,比如,操作系统本身的代码和数据是系统全局可见的,各个进程的地址空间中不可避免地要包含这部分内存。此外,各个进程私有的数据和代码存放在LDT中,因而进程切换时,只需改变LDT表,即可实现进程私有地址的切换。在现代的应用程序中,每个进程往往包含多个二进制模块,以及一些动态数据区;各个二进制模块中既有代码,也有数据(比如全局变量和静态变量等)。所以,如果用段机制来管理内存的话,每个模块都需要一个段,动态数据区往往需要一个或多个段(比如全局堆和局部堆,以及栈等)。对于绝大多数应用程序,8192个段(指LDT中的段)足够使用了。对于系统全局空间,只要小心地安排好段的使用,8192个段(指GDT中的段)也能满足正常的内存分配。利用这种方法,既可以做到进程之间的空间隔离性,也可以很方便地在进程之间共享数据。
实际上在windows和linux操作系统中,都没有使用复杂的分段机制来管理内存。因此在理解linux和windows操作系统的内存管理中,关键是理解下面的一步“线性地址到物理地址”的转换。
(三)&&&线性地址到物理地址的转换
线性地址到物理地址的转换过程也被称为“分页机制”。
首先“分页机制”把物理内存划分为若干”页”的模式来处理,普通情况下一页的内存大小是4K,也就是说,操作系统首先把物理内存按照4K大小划分为页,线性地址本质上指向了某页中的某个位置。线性地址到物理地址的映射过程是把线性地址分成若干部分,每个部分的值是一个表的索引,通过几次查表最终得到物理地址的实际位置(上一级的表的内容指向下一级表的起始地址),一个分页的示意图如下所示。
其中Page Directory和Page
Table中存放的内容是一模一样的,每项内容由12bits的标志和20bits的物理地址页构成(为什么20Bits就可以标示物理地址页了?因为采用4K页的时候,4G的物理地址最大分成2^20块,因此20bits就可以标示一个物理地址页了)。每项内容的大小是4字节,而每个目录表都是采用4K页面存放的(因为4K页面是一个物理地址的最小分块),这样一个目录表中可以存放1024个不同的数据内容,则可以最大支持10bits的地址线的索引。
从线性地址到物理地址的基本过程如下:
1. 首先把第一级目录表的首地址存放在CR3寄存器中;
把32位地址分成3个部分,前10bits作为一级目录的索引,中间10bits作为二级目录的索引,低12bits作为一页的偏移;
3. 通过2次索引,查到了某个线性地址对应的物理地址了。
上述分页机制是X86的常规分页模式,不同分页模型的关键区别:
地址线的长度;地址的长度决定了操作系统层面最大可以支持的内存数量,这个是由CPU和操作系统决定的。很明显常规分页模型是针对32bits的地址线长度。对32bits的地址线长度而言,最大寻址空间为2^32
= 4G。例如从Pentium
Pro处理器开始,CPU支持36bits的物理地址长度(理论上支持64G物理内存),X86-64支持48bits的物理地址长度(理论支持256T的物理内存)。当物理地址长度大于32bits的时候,按照4K分页,则采用20bits标示一个物理页就不够了,因此分级目录表中每个数据的长度就需要用64bits来标示,则4K大小的内存空间只能存放512
= 2^9个表项。
2. 分几级目录来转换。首先思考为什么要分目录级别呢?分目录级别的目的就是为了节省空间,很明显第一级目录(常规分页中Page
Directory)的表项空间必须是连续的,并且是在开始必须分配好。而二级目录的(常规分页中的Page
Table)的表项空间不是必须分配的,只要在Page
Directory中的某个索引中需要指向二级目录的时候,再准备好二级目录的表项空间即可,这样就节省了很多空间。本质上就是一个二维指针,第一维的指针数组的内容可能是NULL,当不是NULL的时候,才指向另外一个同样大小的指针数组。如果线性地址的寻址空间越大,则需要的目录表的空间就越大。
3. 分页的大小。常规分页情况下,每页的大小是4K,因此需要12bits来标示最后在页内的offset,因为2^12 =
4096 = 4K。因此,如果分页的大小不一样,则最后的OFFSET的字段的位数不一样。
下表就是在64位CPU的分页机制:
在X86-64的架构下,地址线的长度是48bits,这样标示一个物理分页需要36bits,再加上12bits的每页的标志位,则每个目录页(4K大小)最大存放512个内容。因此,X86-64下需要采用4级目录的模式,每个目录项的表空间还是4K,但是存放512
= 2^9个不同的内容项。把48bits分成9 + 9 + 9 + 9 + 12的分页模型。
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。2010年5月 VC/MFC大版内专家分月排行榜第三
2010年5月 VC/MFC大版内专家分月排行榜第三
2011年 总版技术专家分年内排行榜第三2010年 总版技术专家分年内排行榜第三
2012年 总版技术专家分年内排行榜第五
本帖子已过去太久远了,不再提供回复功能。新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
本帖最后由 lofeng410 于
11:07 编辑
内核中定义的宏
#define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK)
从这个宏来看,slab不能从DMA32区域分配页面。
DMA32是x86-64系统上才存在的区域,DMA32也是低端内存区域,16M-4G。
为何slab分配器也不能从该区域分配页面呢?
求大侠指点一二
这个帖子中也提到:
当时提到的时高端内存与低端内存,并没有涉及DMA32区域
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
对于x86_64,如果只有2G的物理内存,这个时候都是DMA DMA32区域,此时怎么给slab分配页面呢?
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
自己顶起~
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
自己顶起~~
稍有积蓄, 积分 216, 距离下一级还需 284 积分
论坛徽章:0
磁盘不经过MMU内存寻址,他只能寻找32MB的DMA去的内存物理地址,如果slab把这个区域的内存分配的差不多了,磁盘怎么与内存怎么读写数据?
不记得看的是那本书了,大概是这样,提供一个思路,LZ可以去查一下,应该没有记错。
富足长乐, 积分 7205, 距离下一级还需 795 积分
论坛徽章:1
本帖最后由 linuxfellow 于
23:37 编辑
这是一个内存分配的概念问题
这里ZONE_NORMAL/ZONE_DMA32指的是地址空间,不是物理内存
有slab分配的物理内存,会得到ZONE_NORMAL的线性地址, 不会得到ZONE_DMA32的线性地址;而这部分内存的物理地址不一定和线性地址有直接映射关系。
如果系统只有2G内存,程序要用到比2G大的地址空间,这是就不能完全直接映射;就会根据需要,有一部分映射到ZONE_NORMAL, 有一部分映射到ZONE_DMA32。
如果系统只有2G内存, 你的程序地址空间也只用2G以内的地址,你可以调整ZONE_NORMAL/ZONE_DMA32/DMA范围,让他和物理地址完全匹配,这样整个系统就可以直接映射了。
64位机线性地址空间足够大,完全可以把内核区和dma区分开。因为dma传送很容易出问题,在不同的区时,即使dma出了错,也不大可能波及内核敏感数据,增加系统安全性。
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
linuxfellow
在一个768M内存的系统上,只有DMA和DMA32两个区域,而没有其他zone,这个怎么理解呢?
nvp:~/Desktop # cat /proc/buddyinfo
Node 0, zone& && &DMA& && &1& && &3& && &2& && &1& && &2& && &1& && &0& && &0& && &1& && &1& && &3
Node 0, zone& & DMA32& &&&74& &&&14& && &5& && &1& && &2& && &2& && &1& && &1& && &1& &&&10& &&&44
nvp:~/Desktop # free -m
& && && && & total& && & used& && & free& &&&shared& & buffers& &&&cached
Mem:& && && &&&680& && &&&471& && &&&208& && && & 0& && && &20& && &&&291
-/+ buffers/cache:& && &&&159& && &&&520
Swap:& && && &1121& && && & 0& && & 1121
论坛徽章:16
ZONE_DMA 0~16MB
ZONE_DMA32 16MB~4GB
稍有积蓄, 积分 294, 距离下一级还需 206 积分
论坛徽章:0
本帖最后由 lofeng410 于
23:40 编辑
embeddedlwp
恩 ,这两个zone的物理空间确实是这样划分的。
我贴出的那个768M系统中,也确实就只有这两个zone。
我想表明的是,ZONE_NORMAL/ZONE_DMA32应该指的是物理空间,而不是地址空间。也没有看到可以调整这几个zone的大小的地方,所以很困惑6楼所说的。
这个是否死活只有2个zone,真不知道slab要从哪里分配内存
#define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK)
这个宏定义限定死了不能从DMA32 zone申请,而DMA只有16M,slab的空间究竟是从哪里分配的呢?
论坛徽章:16
本帖最后由 embeddedlwp 于
11:39 编辑
6楼表述的不准确,
这里ZONE_NORMAL/ZONE_DMA32指的是地址空间,不是物理内存
==================================
应该是物理内存
有slab分配的物理内存,会得到ZONE_NORMAL的线性地址, 不会得到ZONE_DMA32的线性地址;而这部分内存的物理地址不一定和线性地址有直接映射关系。
==================================
如果zone_normal内存比较少,一样会fallback到zone_dma32,
如果系统只有2G内存,程序要用到比2G大的地址空间,这是就不能完全直接映射;就会根据需要,有一部分映射到ZONE_NORMAL, 有一部分映射到ZONE_DMA32。
==================================
如果是x86_64,并且是2G memory,那么zone_normal的大小为0,16MB~2GB都会映射到zone_dma32,内核的直接映射当然映射到这个区域啦。
平台 x86_64, 8GB memory, 在boot命令行添加mem=4G
cat /proc/zoneinfo
Node 0, zone& && &DMA
&&pages free& &&&3971
& && &&&min& && &59
& && &&&low& && &73
& && &&&high& &&&88
& && &&&scanned&&0
& && &&&spanned&&4080
& && &&&present&&3911
& & nr_free_pages 3971
& & nr_inactive_anon 0
& & nr_active_anon 0
& & nr_inactive_file 0
& & nr_active_file 0
& & nr_unevictable 0
& & nr_mlock& &&&0
& & nr_anon_pages 0
& & nr_mapped& & 0
& & nr_file_pages 0
& & nr_dirty& &&&0
& & nr_writeback 0
& & nr_slab_reclaimable 0
& & nr_slab_unreclaimable 4
& & nr_page_table_pages 0
& & nr_kernel_stack 0
& & 。。。。。。。。。。。。。。。。
Node 0, zone& & DMA32
&&pages free& &&&564785
& && &&&min& && &11204
& && &&&low& && &14005
& && &&&high& &&&16806
& && &&&scanned&&0
& && &&&spanned&&744704
& && &&&present&&731710
& & nr_free_pages 564785
& & nr_inactive_anon 22420
& & nr_active_anon 37544
& & nr_inactive_file 62407
& & nr_active_file 8731
& & nr_unevictable 878
& & nr_mlock& &&&878
& & nr_anon_pages 24786
& & nr_mapped& & 10337
& & nr_file_pages 95390
& & nr_dirty& &&&7
& & nr_writeback 0
&&&&nr_slab_reclaimable 4989
& & nr_slab_unreclaimable 9217
& & nr_page_table_pages 2881
& & nr_kernel_stack 319
& & 。。。。。。。。。。。。。。
平台 x86_64, 8GB memory,&&
cat /proc/zoneinfo
Node 0, zone& && &DMA
&&pages free& &&&3971
& && &&&min& && &32
& && &&&low& && &40
& && &&&high& &&&48
& && &&&scanned&&0
& && &&&spanned&&4080
& && &&&present&&3911
& & nr_free_pages 3971
& & nr_inactive_anon 0
& & nr_active_anon 0
& & nr_inactive_file 0
& & nr_active_file 0
& & nr_unevictable 0
& & nr_mlock& &&&0
& & nr_anon_pages 0
& & nr_mapped& & 0
& & nr_file_pages 0
& & nr_dirty& &&&0
& & nr_writeback 0
& & nr_slab_reclaimable 0
& & nr_slab_unreclaimable 4
& & nr_page_table_pages 0
& & nr_kernel_stack 0
&&。。。。。。。。。。。。。。。。。。。。
Node 0, zone& & DMA32
&&pages free& &&&723588
& && &&&min& && &6097
& && &&&low& && &7621
& && &&&high& &&&9145
& && &&&scanned&&0
& && &&&spanned&&1044480
& && &&&present&&727026
& & nr_free_pages 723588
& & nr_inactive_anon 0
& & nr_active_anon 0
& & nr_inactive_file 0
& & nr_active_file 0
& & nr_unevictable 0
& & nr_mlock& &&&0
& & nr_anon_pages 0
& & nr_mapped& & 1
& & nr_file_pages 0
& & nr_dirty& &&&0
& & nr_writeback 0
& & nr_slab_reclaimable 0
& & nr_slab_unreclaimable 0
& & nr_page_table_pages 0
& & nr_kernel_stack 0
。。。。。。。。。。。。。。。。。&&
Node 0, zone& &Normal
&&pages free& &&&1107642
& && &&&min& && &10765
& && &&&low& && &13456
& && &&&high& &&&16147
& && &&&scanned&&0
& && &&&spanned&&1304064
& && &&&present&&1283688
& & nr_free_pages 1107642
& & nr_inactive_anon 22419
& & nr_active_anon 36924
& & nr_inactive_file 62017
& & nr_active_file 8633
& & nr_unevictable 878
& & nr_mlock& &&&878
& & nr_anon_pages 23105
& & nr_mapped& & 10296
& & nr_file_pages 94895
& & nr_dirty& &&&0
& & nr_writeback 0
& &nr_slab_reclaimable 5002
& & nr_slab_unreclaimable 9112
& & nr_page_table_pages 2792
& & nr_kernel_stack 300
。。。。。。。。。。。。。。。
#define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK)
至于这个宏,应该是表示调用kmem_cache_alloc的时候不能传递__GFP_DMA32,__GFP_HIGHMEM等作为flag.
从上边实验的数据来看,已经从zone_dma分配了slab page,并且是unreclaimable类型的4个页。
北京皓辰网域网络信息技术有限公司. 版权所有 京ICP证:060528号 北京市公安局海淀分局网监中心备案编号:
广播电视节目制作经营许可证(京) 字第1234号
中国互联网协会会员&&联系我们:
感谢所有关心和支持过ChinaUnix的朋友们
转载本站内容请注明原作者名及出处

我要回帖

更多关于 x86最大内存 的文章

 

随机推荐