linux虚拟地址空间

来源:互联网 发布:最有深意的话知乎 编辑:程序博客网 时间:2024/05/21 14:53

      虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被处理器引用。内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个页错误(page fault)。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化。



 Linux进程在虚拟内存中的标准内存段布局如下图所示:




用户进程部分分段存储内容如下表所示(按地址递减顺序):


 以下详细介绍各个分段的含义

一、栈(stack)
由编译器自己自动分配释放。
三个用途:
          1、为函数内部声明的非静态局部变量分配内存。
          2、记录函数调用的相关维护信息。称为栈帧。包括函数返回地址、不适合装入寄存器的函数参数和寄存器值。
          3、临时存储区。

linux中ulimit -s命令可以查看和和设置栈的最大值。
栈的大小在运行时由内核动态调整。

二、内存映射区(mmap)
内核将硬盘文件直接映射到内存,任何程序都可以通过linux的mmap请求这种映射。
三、堆(heap)
1、用于存储进程运行时的内存段,可动态扩张和缩减。堆中内容是匿名的不能按名字访问,只能通过指针访问。
2、分配的堆内存也是字节对齐的空间,适合原子操作。
3、堆申请和释放是无序的,最终会产生内存碎片。
使用堆时遇到的两个问题:
                                   1、释放或改写正在使用的内存(内存破坏)。
                                   2、没有释放已经不使用的内存(内存泄漏)。

四、BSS段
1、未初始化的全局变量和静态局部变量。
2、初始值为0的全局变量和静态局部变量。
3、未定义且初值不为0的符号。
4、BSS仅为未初始化的全局变量和静态的局部变量预留位置,在目标文件中不占空间。
5、C语言中,未显式初始化的全局便量和静态局部变量被初始化为0(算术类型)或空指针(指针类型)。
6、BSS段不包含数据,仅维护开始和结束地址,以便内存能在运行时被有效地清零。BSS所需的运行时空间由目标文件记录,但BSS并不占用目标文件内的实际空间。
五、数据段
1、数据段通常用于存储程序中初始值不为0的全局变量和静态局部变量。
2、数据段属于静态内存分配(静态存储),可读可写。
3、数据段保存在目标文件中,其内容由程序初始化。
4、数据段和BSS段的区别:
                     1、BSS段不占用物理文件尺寸,但占用内存空间;数据段占用物理文件,也占用内存空间。
                     2、当程序读取数据段的数据时,系统会出发缺页故障,从而分配相应的物理内存;当程序读取BSS段的数据时,内核会将其转到一个全零页 面,不会发生缺页故障,也不会为其分配相应的物理内存。 
5、运行时数据段和BSS段的整个区段通常称为数据区。某些资料中“数据段”指代:数据段 + BSS段 + 堆。
六、文本段(text)
1、代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。
2、代码段指令根据程序设计流程依次执行,对于顺序指令,只会执行一次(每个进程);若有反复,则需使用跳转指令;若进行递归,则需要借助栈来实现。
3、代码段指令中包括操作码和操作对象(或对象地址引用)。若操作对象是立即数(具体数值),将直接包含在代码中;若是局部数据,将在栈区分配空间,然后引用该数据地址;若位于BSS段和数据段,同样引用该数据地址。
4、代码段最容易受优化措施影响。

堆和栈的区别
管理方式:栈由编译器自动管理;堆由程序员控制,使用方便,但容易产生内存泄漏。
生长方向:栈向低地址扩展(向下生长),是连续的内存区域;堆向高地址扩展(向上生长),是不连续的,系统用链表存储空闲的内存地址,链表从低地址向高地址遍历。
空间大小:栈顶地址和栈的最大容量由系统预先设定(默认2M或者10M);堆的大小受限于计算机系统中有效的虚拟内存,32位linux系统中堆内存可达2.9G。
存储内容:栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量,本次调用结束后,局部变量先出栈,然后是参数,栈顶指针指向最开始存的指令地址,程序由该点继续执行下条可执行语句。堆通常在头部用一个字节存放其大小, 堆用于存储生存期与函数调用无关的函数,具体内容由程序员安排。
分配方式:栈可静态分配或者动态分配,静态分配由编译器分配,比如局部变量的分配,动态分配由alloca(内存分配函数 )在栈上分配,用完后自动释放。堆只能动态分配且手工释放。
分配效率:栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈有专门的指令执行;堆由函数库提供,机制复杂,效率比栈低的多。
操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间。
此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空闲链表中。
碎片问题:栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。而频繁申请释放操作会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。


参考博客:http://www.cnblogs.com/clover-toeic/p/3754433.html



原创粉丝点击