ImageVerifierCode 换一换
格式:DOCX , 页数:11 ,大小:148.74KB ,
资源ID:4451601      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/4451601.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux的内存管理 地址解析寻址.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Linux的内存管理 地址解析寻址.docx

1、Linux的内存管理 地址解析寻址Linux的内存管理Linux的内存管理主要分为两部分:物理地址到虚拟地址的映射,内核内存分配管理(主要基于slab)。一、物理地址与虚拟地址之间的映射1.首先,介绍涉及到的几个地址的概念。物理地址(physical address)用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。

2、所以,说它是“与地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。虚拟内存(virtual memory),这是对整个内存(不要与机器上插那条对上号)的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存,例如,一个0x08000000内存地址,它并不对应物理地址上那个大数组中0x08000000 - 1那个地址元素;之所以是这样,是因为现代操作系统都提供了一种内存管理的抽像,即虚拟内存(virtual memory)。进程使用虚拟内存中的地址,由操作系统协助相关硬件,

3、把它“转换”成真正的物理地址。这个“转换”,是所有问题讨论的关键。有了这样的抽像,一个程序就可以使用比真实物理地址大得多的地址空间。(拆东墙,补西墙,银行也是这样子做的),甚至多个进程可以使用相同的地址。不奇怪,因为转换后的物理地址并非相同的。可以把连接后的程序反编译看一下,发现连接器已经为程序分配了一个地址,例如,要调用某个函数A,代码不是call A,而是call 0x0811111111 ,也就是说,函数A的地址已经被定下来了。没有这样的“转换”,没有虚拟地址的概念,这样做是根本行不通的。逻辑地址(logical address)Intel为了兼容,将远古时代的段式内存管理方式保留了下来

4、。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上例,我们说的连接器为函数A分配的0x08111111这个地址就是逻辑地址。不过不好意思,这样说,好像又违背了Intel中段式管理中,对逻辑地址要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为段标识符:段内偏移量,也就是说,上例中那个0x08111111,应该表示为A的代码段标识符: 0x08111111,这样,才完整一些”。线性地址(linear address)或也叫虚拟地址(virtual address)跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换

5、前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!),CPU 要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel完全是为了兼容而已。2.CPU段式内存管理,逻辑地址如何转换为线性地址一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。

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

7、东很复杂,虽然可以利用一个数据结构来定义它,不过,我这里只关心一样,就是Base字段,它描述了一个段的开始位置的线性地址。Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段 描述符表(LDT)”中。那究竟什么时候该用GDT,什么时候该用LDT呢?这是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。好多概念,像绕口令一样。这张图看起来要直观些:首先,给定一个完整的逻辑地址段选择符:段内偏移地址,a、看段

8、选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。b、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。c、把Base + offset,就是要转换的线性地址了。还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。OK,来看看Linux怎么做的。3.Linux的段式管理Intel要求两次转换,这样虽说是兼容了,但是确实很冗余,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一样。另一方面,其它某些硬件

9、平台,没有二次转换的概念,Linux也需要提供一个高层抽像,来提供一个统一的界面。所以,Linux的段式管理,事实上只是“哄骗”了一下硬件而已。按照Intel的本意,全局的用GDT,每个进程自己的用LDT不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段,对应的内核中的是内核数据段和内核代码段。“在Linux下,逻辑地址与线性地址总是一致(是一致,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。!”忽略了太多的细节,例如段的权限检查。呵呵。Linux中,绝大部份进程并不例用LDT,除非使用Wine ,仿真Windows程序的时候

10、。4.CPU的页式内存管理CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页(page),例如一个32位的机器,线性地址最大可为4G,可以用4KB为一个页来划分,这页,整个线性地址就被划分为一个 tatol_page220的大数组,共有2的20个次方个页。这个大数组我们称之为页目录。目录中的每一个目录项,就是一个地址对应的页的地址。另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。这里注意到,这个total_page数组

11、有220个成员,每个成员是一个地址(32位机,一个地址也就是4字节),那么要单单要表示这么一个数组,就要占去4MB的内存空间。为了节省空间,引入了一个二级管理模式的机器来组织分页单元。文字描述太累,看图直观一些:如上图所示1、分页单元中,页目录是唯一的,它的地址放在CPU的CR3寄存器中,是进行地址转换的开始点。万里长征就从此长始了。2、每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。运行一个进程,需要将它的页目录地址放到cr3寄存器中,将别个的保存下来。3、每一个32位的线性地址被划分为三部份,页目录索引(10位):页表索引(10位)

12、:偏移(12位)。210 * 4B + 210 * 4 B = 8KB.依据以下步骤进行转换:1、从CR3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);2、根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。3、根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;4、将页的起始地址与线性地址中最后12位相加,得到最终我们想要的葫芦;这个转换过程,应该说还是非常简单地。全部由硬件完成,虽然多了一道手续,但是节约了大量的内存,还是值得的

13、。那么再简单地验证一下:1、这样的二级模式是否仍能够表示4G的地址;页目录共有:210项,也就是说有这么多个页表.每个目表对应了:210页;每个页中可寻址:212个字节。还是232 = 4GB2、这样的二级模式是否真的节约了空间;也就是算一下页目录项和页表项共占空间(210 *4 + 2 10 *4) =8KB。按中的解释,二级模式空间的节约是从两个方面实现的:A、如果一级页表中的一个页表条目为空,那么那所指的二级页表就根本不会存在。这表现出一种巨大的潜在节约,因为对于一个典型的程序,4GB虚拟地址空间的大部份都会是未分配的;B、只有一级页表才需要总是在主存中。虚拟存储器系统可以在需要时创建,

14、并页面调入或调出二级页表,这就减少了主存的压力。只有最经常使用的二级页表才需要缓存在主存中。不过Linux并没有完全享受这种福利,它的页表目录和已分配页面相关的页表都是常驻内存的。值得一提的是,虽然页目录和页表中的项,都是4个字节,32位,但是它们都只用高20位,低12位屏蔽为0把页表的低12屏蔽为0,是很好理解的,因为这样,它刚好和一个页面大 小对应起来,大家都成整数增加。计算起来就方便多了。但是,为什么同时也要把页目录低12位屏蔽掉呢?因为按同样的道理,只要屏蔽其低10位就可以了,不过我想,因为1210,这样,可以让页目录和页表使用相同的数据结构,方便。5.Linux的页式内存管理原理上来

15、讲,Linux只需要为每个进程分配好所需数据结构,放到内存中,然后在调度进程的时候,切换寄存器CR3,剩下的就交给硬件来完成了。前面说了i386的二级页管理架构,不过有些CPU,还有三级,甚至四级架构,Linux为了在 更高层次提供抽像,为每个CPU提供统一的界面。提供了一个四层页管理架构,来兼容这些二级、三级、四级管理架构的CPU。这四级分别为:页全局目录PGD(对应刚才的页目录)页上级目录PUD(新引进的)页中间目录PMD(也就新引进的)页表PT(对应刚才的页表)。整个转换依据硬件转换原理,只是多了二次数组的索引罢了,如下图:那么,对于使用二级管理架构32位的硬件,现在又是四级转换了,它们

16、怎么能够协调地工作起来呢?嗯,来看这种情况下,怎么来划分线性地址吧!从硬件的角度,32位地址被分成了三部份也就是说,不管理软件怎么做,最终落实到硬件,也只认识这三位老大。从软件的角度,由于多引入了两部份,也就是说,共有五部份。要让二层架构的硬件认识五部份也很容易,在地址划分的时候,将页上级目录和页中间目录的长度设置为0就可以了。这样,操作系统见到的是五部份,硬件还是按它死板的三部份划分,也不会出错,也就是说大家共建了和谐计算机系统。这样,虽说是多此一举,但是考虑到64位地址,使用四层转换架构的CPU,我们就不再把中间两个设为0了,这样,软件与硬件再次和谐抽像就是强大呀!例如,一个逻辑地址已经被

17、转换成了线性地址,0x08147258,换成二制进,也就是:0000100000 0101000111 001001011000内核对这个地址进行划分,PGD = 0000100000PUD = 0PMD = 0 010*offset = 001001011000现在来理解Linux针对硬件的花招,因为硬件根本看不到所谓PUD,PMD,所以,本质上要求PGD索引,直接就对应了PT的地址。而不是再 到PUD和PMD中去查数组(虽然它们两个在线性地址中,长度为0,20 =1,也就是说,它们都是有一个数组元素的数组),那么,内核如何合理安排地址呢?从软件的角度上来讲,因为它的项只有一个,32位,刚好

18、可以存放与PGD中长度一样的地址指针。那么所谓先到PUD,到到PMD中做映射转换, 就变成了保持原值不变,一一转手就可以了。这样,就实现了“逻辑上指向一个PUD,再指向一个PDM,但在物理上是直接指向相应的PT的这个抽像,因为硬 件根本不知道有PUD、PMD这个东西”。然后交给硬件,硬件对这个地址进行划分,看到的是:页目录 = 0000100000 010*offset = 001001011000先根据0000100000(32),在页目录数组中索引,找到其元素中的地址,取其高20位,找到页表的地址,页表的地址是由内核动态分配的,再加一个offset,就是最终的物理地址了。二、内核内存分配管

19、理内存管理方法应该实现以下两个功能:a.最小化管理内存所需的时间b.最大化用于一般应用的可用内存(最小化管理开销)1.直接堆分配每个内存管理器都使用了一种基于堆的分配策略。在这种方法中,大块内存(称为堆)用来为用户定义的目的提供内存。当用户需要一块内存时,就请求给自己分配一定大小的内存。堆管理器会查看可用内存的情况(使用特定算法)并返回一块内存。搜索过程中使用的一些算法有first-fit(在堆中搜索到的第一个满足请求的内存块)和best-fit(使用堆中满足请求的最合适的内存块)。当用户使用完内存后,就将内存返回给堆。这种基于堆的分配策略的根本问题是碎片(fragmentation)。当内存

20、块被分配后,它们会以不同的顺序在不同的时间返回。这样会在堆中留下一些洞,需要花一些时间才能有效地管理空闲内存。这种算法通常具有较高的内存使用效率(分配需要的内存),但是却需要花费更多时间来对堆进行管理。2.伙伴分配算法另外一种方法称为buddy memory allocation,是一种更快的内存分配技术,解决了内存分配的外碎片问题,它将所有空闲页框分别挂接到11个链表中,每个链表中分别包含大小为1,2,4,8,16,32,64,128,256,512,1024个连续空闲的页框。并使用 best-fit 方法来分配内存请求,然后将分配剩余的页框挂接到空闲块较小的链表中。当用户释放内存时,就会检

21、查 buddy 块,查看其相邻的内存块是否也已经被释放。如果是的话,将合并内存块,并挂接到空闲块较大的链表中。这个算法的时间效率很高,但是由于使用 best-fit 方法的缘故,会产生内存浪费,即内碎片。3.slab分配器一些内存空间,这些空间一般都是结构体。而这些结构体往往都会有一个共同的初始化行为比如:初始化里面的信号量、链表指针、成员。通过Sun 的大牛JeffBonwick 的研究发现,内核对这些结构体的初始化所消耗的时间比分配它们的时间还要长。所以他设计了一种算法,当这些结构体的空间被释放的时候,只是让他回到刚刚分配好的状态而不真正释放,下次再申请的时候就可以节约初始化的时间。整个过

22、程可以理解为借用白板的过程。申请空间就是从别人那里借多块白板。由于每块白板的用处不同,每次用的时候都要先在不同的白板上画上不同的表格,然后往里面填内容。如果一般的算法则是用完白板后,擦掉表格然后还给人家,下次要用的时候再借回来重新画上表格。优化一点的算法就是用完后暂时不还人家,人家要用的时候再还,第二次再要用白板的时候随便取一块白板重新画表格。而使用slab算法就是不用白板的时候擦除表格的内容留下表格,白板也暂时不还人家。下次要用的时候根据用途取出正确的白板,由于表格是现成的直接往里面填内容就可以了。省去了借白板和画表格这两个操作。Slab分配器的实现相当复杂,但原理不难,其核心思想就是“存储

23、池”的运用。内存片段(小块内存)被看作对象,当被使用完后,并不直接释放而是被缓存到“存储池”里,留做下次使用,这无疑避免了频繁创建与销毁对象所带来的额外负载。Slab技术不但避免了内存内部分片(下文将解释)带来的不便(引入Slab分配器的主要目的是为了减少对伙伴系统分配算法的调用次数频繁分配和回收必然会导致内存碎片难以找到大块连续的可用内存),而且可以很好利用硬件缓存提高访问速度。Slab并非是脱离伙伴关系而独立存在的一种内存分配方式,slab仍然是建立在页面基础之上,换句话说,Slab将页面(来自于伙伴关系管理的空闲页框链)撕碎成众多小内存块以供分配,slab中的对象分配和销毁使用kmem_

24、cache_alloc与kmem_cache_free。slab分配器的基本观点*slab分配器把内存区看作对象(object),把包含高速缓存的主内存区划分为多个slab;*slab分配器把对象按照类型分组放进高速缓存,每个高速缓存都是同种类型对象的一种“储备”;*每个slab由一个或多个连续的页框组成,这些页框中包含已分配的对象,也包含空闲的对象;*slab分配器通过伙伴系统分配页框。slab 缓存分配器的优点1)内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。2)slab 分配器还支持通用对象的初始化,从而避免了为同一目的而对一个对象重复进行初始化。3)slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。对一个进程进行内存分配的大体流程如下图所示:

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1