Linux内核内存管理解析.docx
《Linux内核内存管理解析.docx》由会员分享,可在线阅读,更多相关《Linux内核内存管理解析.docx(174页珍藏版)》请在咨信网上搜索。
1、1 Linux x86_64与i386区别之 内存寻址 收藏 2 1 引子毫无疑问,不管是32位,还是64位处理器,所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。对任何一个普通进程来讲,它都会涉及到5种不同的数据段。稍有编程知识的朋友都该能想到这几个数据段种包含有“程序代码段”、“程序数据段”、“程 序堆栈段”等。不错,这几种数据段都在其中,但除了以上几种数据段之外,进程还另外包含两种数据段。下面我们来
2、简单归纳一下进程对应的内存空间中所包含的 5种不同的数据区。代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存种的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作它是不可写的。数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。BSS段:BSS段包含了程序中未初始化全局变量,在内存中 bss段全部置零。堆(heap):堆是用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用fre
3、e等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。栈:栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“”中定义的变量(但不包括static声明的变量,static意味这在数据 段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进先出特 点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把堆栈看成一个临时数据寄存、交换的内存区。静态分配内存就是编译器在编译程序的时候根据源程序来分配内存. 动态分配内存就是在程序编译之后, 运行时调用运行时刻库函数来分配内存的. 静态分配由于是在程
4、序运行之前,所以速度快, 效率高, 但是局限性大. 动态分配在程序运行时执行, 所以速度慢, 但灵活性高。术语BSS已经有些年头了,它是block started by symbol的缩写。因为未初始化的变量没有对应的值,所以并不需要存储在可执行对象中。但是因为C标准强制规定未初始化的全局变量要被赋予特殊的默认值 (基本上是0值),所以内核要从可执行代码装入变量(未赋值的)到内存中,然后将零页映射到该片内存上,于是这些未初始化变量就被赋予了0值。这样做避免 了在目标文件中进行显式地初始化,减少空间浪费(来自Linux内核开发)我们在x86_64环境上运行以下经典程序:#include#incl
5、ude#includeint bss_var;int data_var0=1;int main(int argc,char *argv) printf(below are addresses of types of processs memn); printf(Text location:n); printf(tAddress of main(Code Segment):%pn,main); printf(_n); int stack_var0=2; printf(Stack Location:n); printf(tInitial end of stack:%pn,&stack_var0);
6、 int stack_var1=3; printf(tnew end of stack:%pn,&stack_var1); printf(_n); printf(Data Location:n); printf(tAddress of data_var(Data Segment):%pn,&data_var0); static int data_var1=4; printf(tNew end of data_var(Data Segment):%pn,&data_var1); printf(_n); printf(BSS Location:n); printf(tAddress of bss_
7、var:%pn,&bss_var); printf(_n); char *b = sbrk(ptrdiff_t)0); printf(Heap Location:n); printf(tInitial end of heap:%pn,b); brk(b+4); b=sbrk(ptrdiff_t)0); printf(tNew end of heap:%pn,b); return 0;运行结果:rootkollera updilogs# ./memorybelow are addresses of types of processs memText location: Address of ma
8、in(Code Segment):0x400568_Stack Location: Initial end of stack:0x7fff0e0dc544 new end of stack:0x7fff0e0dc540_Data Location: Address of data_var(Data Segment):0x600bfc New end of data_var(Data Segment):0x600c00_BSS Location: Address of bss_var:0x600c14_Heap Location: Initial end of heap:0xb059000 Ne
9、w end of heap:0xb0590043 2 x86_64体系新变化AMD x86_64的出现,给全新的64位的x86带来了很多结构上的变化: 1)64位整型数 在x86-64中,所有通用寄存器(GPRs)都从32位扩充到了64位,名字也发生了变化。8个通用寄存器(eax, ebx, ecx, edx, ebp, esp, esi, edi)在新的结构中被命名为rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi,它们都是64位的。呵呵,想当年,从16位扩充到32位时,同样也有一次名字的变化。所有算术逻辑操作、寄存器到内存的数据传输现在都能以64位 的整形类型进
10、行操作。堆栈的压栈和弹出操作都以8字节的单位进行,而且指针类型也拥有了64位。 2)新增寄存器 在新的架构中,另外新增了8个通用寄存器:64位的r8, r9, r10, r11, r12, r13, r14, r15。这样就有利与编译器将函数参数、返回值等放在这些新增的GPR里面进行传递,从而提高了程序的运行速度。同时,128位的MMX寄存器也从原来的 8个增加到了16个。 3)增大的逻辑地址空间 目前在新的架构中,应用程序可以拥有的逻辑地址空间从4GB增加到了256TB(248),而且这一逻辑地址空间在未来可能增加到16EB(264,1EB=1024PB,1PB=1024TB,1TB=102
11、4GB)。 4)增大的物理地址空间 目前的x86-64架构,可以支持的物理内存扩展到了1TB(240),当然,在未来该数字可以扩展到4PB(252)。相比于经过PAE技术扩展的i386的64GB物理内存,新的架构带来了不小的飞跃。 5)无缝使用SSE指令 新的架构借鉴和吸收了Intel的SSE、SSE2的核心指令,并在2005年加入了SSE3。在这一新的架构下,可以不再需要x87浮点协处理器来完成浮点运算了。 6)NX位 跟PAE技术一样,新的x86-64架构也在页表项中增加了NX位,来帮助CPU判断该页包含的内容是否是可以执行的,从而避免借助“buffer overrun”导致的病毒攻击。
12、7)去除旧的机制 在新架构的“长模式(long mode)”下,很多在IA32中被提出,但确不经常被操作系统用到的一些机制不再被支持。这些机制包括段式地址变化机制(FS和GS仍然被保留),任务 转移门(TSS)机制,以及虚拟86模式。当然,出于向下兼容的考虑,x86-64在“传统模式”(Legacy mode)下,仍然对这些机制进行了保留。4 3 x86_64段式管理x86的两种工作模式:实地址模式和虚地址模式(保护模式)。Linux主要工作在保护模式下。在保护模式下,64位x86体系架构的虚地址空间可达248Byte,即256TB,这可比只能到达区区4GB的32位x86体系大多了。逻辑地 址
13、到线性地址的转换由x86分段机制管理。段寄存器CS、DS、ES、SS、FS或GS各标识一个段。这些段寄存器作为段选择器,用来选择该段的描述符。Linux中关于段描述符的宏定义集中在文件/arch/x86/include/asm/Segment.h中,我们先贴出部分代码:32位的:#define GDT_ENTRY_KERNEL_BASE 12 /* 0x0000000c c=1100*/#define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0) /* 0x0000000c c=1100*/#define GDT_ENTRY_KERNEL_DS
14、 (GDT_ENTRY_KERNEL_BASE + 1) /* 0x0000000d c=1101*/64位的:#define GDT_ENTRY_KERNEL32_CS 1 /* 0x00000001 */#define GDT_ENTRY_KERNEL_CS 2 /* 0x00000002 */#define GDT_ENTRY_KERNEL_DS 3 /* 0x00000003 */#define _KERNEL32_CS (GDT_ENTRY_KERNEL32_CS * 8) /* 0x00000100 */#define GDT_ENTRY_DEFAULT_USER32_CS 4 /
15、* 0x00000004 */#define GDT_ENTRY_DEFAULT_USER_DS 5 /* 0x00000005 */#define GDT_ENTRY_DEFAULT_USER_CS 6 /* 0x00000006 */#define _USER32_CS (GDT_ENTRY_DEFAULT_USER32_CS * 8 + 3) /* 0x00000403 */#define _USER32_DS_USER_DS不管32位还是64位的:(我们只关心64位)#define _KERNEL_CS(GDT_ENTRY_KERNEL_CS * 8) /* 0x00000200 */
16、#define _KERNEL_DS(GDT_ENTRY_KERNEL_DS * 8) /* 0x00000300 */#define _USER_DS (GDT_ENTRY_DEFAULT_USER_DS* 8 + 3) /* 0x00000503 */#define _USER_CS (GDT_ENTRY_DEFAULT_USER_CS* 8 + 3) /* 0x00000603 */看见没有,我们熟悉的_USER_CS,_USER_DS,_KERNEL_CS,和_KERNEL_DS,就是传说中的段选择子。我们看到,内核代码段的描述子存放在以0x200为基地址的内存单元中,占8个字节。同样
17、,内核数据段、用户代码段、用户数据段分别存放在以0x300、0x500、0x600为基地址的内存单元中。我们注意到,_USER_DS和_USER_CS的最低三位为3,也就是011,这正说明其CPL位为11,代表用户模式,TI为0,代表GDT。对于x86_64来说,虚拟地址由16位选择子和64位偏移量组成,段寄存器仅仅存放选择子。CPU的分段单元(SU)执行以下操作:1 先检查选择子的TI字段,以决定描述子对应的描述子保存在哪一个描述符表中。TI字段指明描述子是在GDT中(在这种情况下,分段单元从gdtr寄存器中 得到GDT的线性基地址)还是在激活的LDT中(在这种情况下,分段单元从ldtr寄存
18、器中得到LDT的线性基地址)。2 从选择子的13位index字段计算描述子的地址,index字段的值乘以8(一个描述子的大小,其实就是屏蔽掉末尾那三位指示特权级的CPL和指示TI的字段),这个结果与gdtr或ldtr寄存器中的内容相加。3 将对应的段描述子从内存拷贝到CPU的影子Cache中,这样,只有在选择子改变的情况下才会修改影子Cache中的内容。4 把虚拟地址的偏移量与隐Cache中描述子Base字段的值相加就得到了线性地址。例如,为了对内核代码段寻址,内核只需要把_KERNEL_CS宏产生的选择子的值装进cs段寄存器即可。注意,与段相关的线性地址还是从0开始,达到264 -1的寻址限
19、长。这就意味着在用户态或内核态下的所有进程任然使用相同的虚拟地址,这就是传说中的“基本平坦模式”。按照这个模式,虚拟地址跟线性地址数字一样,唯一的不同就是CS和DS装的内容不同,可能是KERNEL级别的选择子,也可能是USER级别的选择子。5 4 x86_64分页管理 虽然逻辑地址扩展到了64位,但是,现有的设计并没有完全用到这64位的空间(264=16EB),因为使用到如此大的空间,势必造成很大的系统开销。AMD64在设计的时候就决定在x86_64的第一阶段,只用这64位中的低48位来做页式地址转换,高16位(48-64位)将填充第 47位相同的内容(这种方式类似于符号扩展)。如果逻辑地址不
20、符合此规定,系统将产生异常。符合此规定的地址称为canonical form,地址的范围分为两段:0 到 00007FFF-FFFFFFFF,以及FFFF8 000-0000 0000到FFFFFFFF-FFFFFFFF,总共为256TB。这种虚拟地址的分层结构,也为操作系统的设计带来了一定便利:可以取地址的上半段保留 做为操作系统的逻辑地址空间,而低地址部分做为装载应用程序的空间,而canonical form不允许的地址空间则做为操作系统的标志、以及特权级的标识等。当然,这样的设计在未来地址进一步扩展的时候将成为一个新的问题。 采用64位地址空间的x86-86被称为是运行在“长模式”(lo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 内核 内存 管理 解析
1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前自行私信或留言给上传者【二***】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时私信或留言给本站上传会员【二***】,需本站解决可联系【 微信客服】、【 QQ客服】,若有其他问题请点击或扫码反馈【 服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【 版权申诉】”(推荐),意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:4008-655-100;投诉/维权电话:4009-655-100。