码迷,mamicode.com
首页 > 其他好文 > 详细

内存区域的划分

时间:2018-10-01 16:32:03      阅读:149      评论:0      收藏:0      [点我收藏+]

标签:tps   alt   分析   module   color   start   文件中   s5pv210   小程序   

本文主要讲解应用层(c/c++内存划分)、linux内核层(X86体系和ARM系统)关于内存上面的划分相关知识点。
一、应用层
1. 在c中分为这几个存储区:堆、栈、全局区(静态区)、常量区
(1).栈 - 由编译器自动分配释放。 栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
(2).堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。 堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
(3).全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
(4).另外还有一个专门放常量的地方,叫做常量区,是存在于代码段的。- 程序结束释放
技术分享图片

从上面的内存模型图中我们可以看出好像和我们上面所说的有出入,其实不然,我们来时,堆和栈区是没有问题的,下面就是全局区(静态区),上面说了未初始化的全局变量和静态变量是在一起的,这个区域叫做BSS段, 初始化的全局变量和静态变量在一块区域 ,是存在于数据段中的,上面BSS段和数据段我们又可以称为数据区,最后就是常量区,它其实是存放在代码段的(只读,在程序装载的时候就已经装载到代码段),因此从上面模型又可以把c语言分为以下几个储存区:堆、栈、数据区、代码段(个人觉得这样划分合理一点)。
BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。代码段是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。

在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的”adgfdf”这样的字符串存放在常量区。比如:
int a = 0; //全局初始化区
char *p1; //全局未初始化区
void main()
{
int b; //栈
char s[] = “abc”; //栈
char *p2; //栈
char *p3 = “123456”; //123456{post.content}在常量区,p3在栈上
static int c = 0; //全局(静态)初始化区
p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
strcpy(p1, “123456”);
//123456{post.content}放在常量区,编译器可能会将它与p3所指向的”123456”优化成一块
}

BSS段的应用:
用cl编译两个小程序如下:

程序1:
int ar[30000];
void main()
{
……
}

程序2:
int ar[300000] = {1, 2, 3, 4, 5, 6 };
void main()
{
……
}

发现程序2编译之后所得的.exe文件比程序1的要大得多。原因就是,一个位于.bss段,而另一个位于.data段,两者的区别在于:全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

由此最终说明了:
1.bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
2.data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。
3.text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。

2.在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
(1).栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
(2).自由存储区,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

C++ 自由存储区是否等价于堆?
(3).堆,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
(4).全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
(5).常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)
堆于自由储存的区别:
自由存储是C++中通过new与delete动态分配和释放对象的抽象概念,而堆(heap)是C语言和操作系统的术语,是操作系统维护的一块动态分配内存。
new所申请的内存区域在C++中称为自由存储区。如果由堆实现的自由存储,可以说new所申请的内存区域在堆上。
堆与自由存储区还是有区别的,它们并非等价,因为自由储存的内存分配不一定是从堆上分配得来。
new/delete实现是运算符,而malloc/free的实现是函数库,明显不同等同来说。
具体解析:http://blog.jobbole.com/109562/

分析:堆和栈的区别。

二、内核层内存分配。
1.x86:将1g的内核虚拟内存地址分别划分为4个区域:直接内存映射区 动态内存映射区 永久内存映射区 固定内存映射区
直接内存映射区 映射关系:在内核初始化(不是动态映射),如果物理内存大于896MB(1g),内核将内核的1g虚拟地址前896mb虚拟内存和物理进行一一映射;1g的虚拟内存还剩余128m,
起始地址:0xC000000大小如果物理内存大于1g,那么直接内存区的大小为896MB,如果物理内存小于896MB,那么物理内存的大小就是直接内存映射区的大小 别名:低端内存
动态内存映射区 映射关系:如果需要某一个物理地址(物理内存地址 寄存器地址),,只需动态的建立物理地址和动态内存映射区的映射关系(页表),即可,每当程序访问这个内核的动态内存映射的虚拟地址其实最终也是访问对应的物理地址(mmu);如果不在使用对应的物理地址,一定要解除映射关系。
起始地址:随着物理内存的大小变化而变化,如果物理内存小于896MB,比如512M,起始地址=0xc000000+物理内存的大小,如果物理内存大于896MB,起始地址0xC0000000+896MB,默认的大小为12MB。
永久内存映射区:映射关系:它也是实现物理地址和内核虚拟地址的动态映射,只是如果在某些时刻,访问这个物理地址的频率很大,如果频繁的建立页表,那么访问效率不高,于是可以将这个物理地址映射到永久内存映射区,一经建立映射关系,无需再销毁对应的映射关系,加快地址的访问速度。当然可以人为的销毁这个映射关系。使用kmap函数进行映射,这个映射有可能会休眠,所以不能再中断上下文使用。大小:4M
固定内存映射区:和永久内存映射区的目的是一样的,区别仅仅是固定内存映射区的虚拟地址映射的时候,可以使用在中断的上下文中,禁止内核抢占。
用户虚拟地址划分:栈 mmap映射区 堆 bbs 数据段 代码段
注意:直接内存映射区又叫低端内存:
动态内存映射区+永久内存映射区+固定内存映射区=高端内存;

2.ARM体系:1G的内核虚拟内存区域的划分:5个区域。异常向量表(vetor)、固定内存映射区、DMA内存映射区、动态内存映射区、直接内存映射区。 注意:arm架构本身的异常向量表的入口地址0xffff0000–0xffff1000和0地址处。3g的用户空间与物理内存访问需要在内核的动态映射区访问。
S5PV210处理器1G内核虚拟地址的划分:Linux内核启动打印的内核信息:
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB) //异常向量表入口地址
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) //固定内存映射区
DMA : 0xff000000 - 0xffe00000 ( 14 MB) //dma内存映射
vmalloc : 0xf4800000 - 0xfc000000 (120 MB) / /动态内存映射区
lowmem : 0xc0000000 - 0xf4000000 ( 832 MB) //低端内存(直接内存映射区)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.init : 0xc0008000 - 0xc003a000 ( 200 kB)
.text : 0xc003a000 - 0xc09ad000 (9676 kB)
.data : 0xc09ae000 - 0xc0a15d20 ( 416 kB)
内核启动的时候将会把内核的1g内存映射进行初始化,页表初始化放在内存中。
虚拟地址 = (物理地址–物理地址的起始地址) + 0xC0000000 ;

--------------------- 本文来自 嵌入式小青年 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/a1299600490/article/details/77119939?utm_source=copy 

内存区域的划分

标签:tps   alt   分析   module   color   start   文件中   s5pv210   小程序   

原文地址:https://www.cnblogs.com/duan-decode/p/9734871.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!