本篇介绍一些Linux的内存管理.
1. 进程的内存空间分配
1.1 进程与程序的区别
(1) 程序是保存在磁盘上的可执行文件
(2) 运行程序时, 需要将可执行文件加载到内存, 形成来进程
(3) 一个程序可以同时对应多个进程
1.2 进程在内存中的布局
![ae2a2dd4ea30ef575c11b29728e441b6.png](https://i-blog.csdnimg.cn/blog_migrate/ddc6ef63a2053d36868fd5f1a7d973c7.jpeg)
(1) text段(代码段 code segment/text segment)
- 通常是指用来存放程序执行代码的一块内存区域, 这部分区域的大小在程序运行前就已经确定, 该内存区域通常属于只读.
- 在代码段中, 包含一些只读的常数变量, 例如字符串常量等, 具有常属性且被初始化的全局变量.
(2) data 段(数据段 data segment)
- 通常是指用来存放程序中已初始化的全局变量的一块内存区域, 该内存区域通可读可写.
- 在数据段中, 包含已初始化全局变量, 已初始化全局静态变量, 局部静态变量.
(3) bss 段(bss段 block started by symbol segment)
- 通常是指用来存放程序中未初始化的全局变量的一块内存区域, 该内存区域通可读可写.
- 在bss段中, 包含未已初始化全局变量, 未初始化全局静态变量.
(4) 堆(heap)
- 堆是用于存放进程运行中被动态分配的内存段, 它的大小并不固定, 可动态扩张或缩减.
- 当进程调用malloc等函数分配内存时, 新分配的内存就被动态添加到堆上(堆被扩张).
- 当进程利用free等函数释放内存时, 被释放的内存从堆中被剔除(堆被缩减).
(5) 栈(stack)
- 栈又称堆栈,是用户存放程序临时创建的局部变量, 也就是函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量).
- 除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中
- 由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场.
- 可以把堆栈看成一个寄存、交换临时数据的内存区.
![b7eb5d28a1c1561001267946f3cb0e35.png](https://i-blog.csdnimg.cn/blog_migrate/80f9061ba8d7483115cea0e6a4d99085.jpeg)
1.3. 进程的获取
(1) 操作系统里每打开一个程序都会创建一个进程ID, 即PID(Process Identification), 可
以使用 getpid() 函数和获取所在的进程 ID.
(2) 当一个进程运行期间, 会在操作系统/proc 目录下自动产生一个以对应进程 ID命名的文件夹,其中maps文件可以查看内存的分布情况.
cat /proc/2831/maps第一列起始地址, 第二列队前面地址可以指向的操作, 第三列该段结束地址.
3. 虚拟地址空间机制
![3d3f3f8d0b91e1e3920695697a1dcc11.png](https://i-blog.csdnimg.cn/blog_migrate/874005eb8c5855c949d8122701c9b75d.jpeg)
(1) 虚拟地址
- linux 编程的时候使用的地址都是虚拟地址
- 可以将虚拟地址视作房间的号牌, 而物理地址即为房间具体的位置
- 每个进程有独立的 0~3G 的地址空间, 进程间共享同一个内核, 内核使用 3G~4G 的虚拟地址空间
- 每个进程有独立的地址映射表
- linux 内核管理内存时的基本单位是页(page), 32bit 机器上内存叶的大小为 40968 即为4K, 可以使用 getpagesize() 函数可获取 page 的大小
(2) 使用虚拟地址的原因
- 给每个进程一个比实际物理内存更大的地址空间
- 便于内存管理
- 同一个 a.out 运行两次, 其中的变量虚拟地址相同, 但是两个进程不会产生相互干扰, 原因在于使用的是虚拟地址, 并且每个地址的地址映射表不同
4. 内存管理的相关函数
(1) 动态分配内存的函数
STL allocator : 内存分配回收自动管理
C++ : new delete 两个运算符
C : malloc free 两个函数
linux/unix : brk(malloc less than 128K) / mmap(malloc more than128K)
内核态 : get_free_page
(2) mmap 函数的作用
- 动态申请内存
- 映射文件 -> 映射普通文件, 映射显存设备
(3) malloc 函数使用时注意点:
- malloc / free 成对出现
- 未执行 malloc 进程中没有堆区的
- 执行 malloc 后, 系统会最少给当前进程分配 33 page
- 每次 malloc 时, 如果向申请 4 个字节的空间, 实则耗费了 16 个字节的空间其中多出来的 12 个字节用于存储该次 malloc 的附加信息
(4) 段错误的产生
在使用内存过程中不要越界, 如果越界访问可能引起系统问题, 产生段错误, 其产生的原因
- 对内存区域进行没有权限的操作, 比如修改只读区
- 访问未映射的虚拟地址内存空间
char* p=NULL;*p='a';
5.static和const关键字
(1) static 关键字
修饰局部变量 :该局部变量放入数据段, 该局部变量在整个进程中始终有效
修饰全局变量 :该变量还是在数据段里, 该变量在整个进程中始终有效, 该变量的作用域只限于本模块内
修饰函数 :本模块可见
(2) const 修饰指针
- 常量指针
int const *p;
p++ const 修饰*p, 所以*p 不能修改, 但是 p可以修改
- 指针常量
int *const p;
*p++ const 修饰 p, 所以 p 不能修改, 但是*p可以修改