最近在看李先静老师的《系统程序员成长计划》的书,李老师也在他的博客中写了有关内容:http://blog.youkuaiyun.com/absurd。当然这里所说的linux内存模型也是李老师博客中提到。这里自己再写出来,不是说自己写的比李老师好,而是为了增加自己的理解,习惯性的将东西写出来。
在说linux中内存模型之前,我们先了解几个数据存放的概念:
1. .bss段
BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。不过不是所有的平台都会把为初始换的静态变量自动清0的,所以手动的将未初始化的静态变量清零是一个比较好的习惯。
2. .data段
这个是和.bss段相对应的,它是用来存放初始化的全局变量和静态变量。当然和.bss段一样,他们都是全局变量,在整个程序的运行周期内,它们都是一直存在的。
3. .rodata段
它是read only data的简称,也就是它是用来存放常量数据的。关于rodata类型的数据,需要注意一下几点:(李老师博客中有说)
(1)常量不一定存放在rodata段中,有的立即数直接和指令编码一起,存放在代码段(.text)中
(2)对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO,SO是共享库的文件类型)中只存在一个副本。
(3)在有的嵌入式系统中, rodata存放在ROM里,运行时直接读取,无需加载到RAM中。
(4)在嵌入式Linux系统中,也可以通过一种叫做XIP(eXecute In Place,即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM。)的技术直接读取常量数据,而无需加载到RAM中。
(5)常量是不能修改的,修改常量在Linux下会出现段错误(SEGMENT FAULT)。
(6)rodata是在多个进程间共享的,这样可以提高运行空间利用率。
4. 代码段(.text)
代码段可以存放代码和部分整数常量,它是可以执行的,它的特性和rodata比较类似。
5. 栈(stack)
栈是一种常见的数据结构,它可以实现一些比较好的功能。比如它可以解析递归的过程,可以利用栈进行非递归的计算。这里我们说的不是数据结构,而是一种内存存储数据的方式。它是向下生长的,这是因为不同平台对栈的最大值做了规定,在cpu中通过ESS(栈寄存器),EBP(基址寄存器),ESP(栈顶寄存器)来操作栈的。需要注意的是,不同线程或进程都有自己私有的栈,这些栈在程序退出的时候会自动销毁。
6. 堆(heap)
堆是一种灵活的内存,它的生命周期完全由使用者控制。它和栈不同的地方包括:它需要使用者进行手动的分配和收回,它是向上生长的。标准C提供了一下几个函数来使用堆内存。
malloc :用来分配一块指定大小的内存
realloc :用来重新分配一块内存
calloc :用来分配一块指定大小的内存,并进行清0的初始化
free :回收不再使用的堆内存
alloc : alloc函数不同于其他分配堆内存函数的是:函数afree和函数alloc是“不完善”的,是因为对afree函数的调用次序必须与调用alloc函数的次序相反。换句话说,alloc与afree以栈的方式(即后进先出的列表)进行存储空间的管理
下面是linux内存模型,这是“偷”李老师博客中的,
Linux的内存模型,一般为:
地址 | 作用 | 说明 |
>=0xc000 0000 | 内核虚拟存储器 | 用户代码不可见区域 |
<0xc000 0000 | Stack(用户栈) | ESP指向栈顶 |
| ↓
↑ |
空闲内存 |
>=0x4000 0000 | 文件映射区 |
|
<0x4000 0000 |
↑ |
空闲内存
|
| Heap(运行时堆) | 通过brk/sbrk系统调用扩大堆,向上增长。 |
| .data、.bss(读写段) | 从可执行文件中加载 |
>=0x0804 8000 | .init、.text、.rodata(只读段) | 从可执行文件中加载 |
<0x0804 8000 | 保留区域 |
|
未完待续,先去吃饭