内存划分
- 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是
8 MB
- 文件映射段,包括动态库、共享内存等,从低地址开始向上增长
- 堆段,包括动态分配的内存,从低地址开始向上增长;
- BSS 段,包括未初始化的静态变量和全局变量;
- 数据段,包括已初始化的静态常量和全局变量;
- 代码段,包括二进制可执行代码;
虚拟内存
1.引出
- 多个程序访问同一块物理内存, 写数据存在将对方内存覆盖的情况 --> 不同程序所写的内存应该隔离 --> 虚拟内存 + 使用MMU(内存管理单元)将虚拟地址与物理地址联系起来
- 这样就可以同时跑多个程序, 不会出现问题
2.功能
- a.保证进程的独立性
- 虚拟地址空间 + 页表 -- 页表私有, 每个进程的虚拟内存空间就是相互独立的(解决多进程地址冲突问题)
- b.提高资源利用率
- 虚拟地址空间 + 写时拷贝 -- 只有发生写数据的时候才会拷贝数据
- c.保护了物理内存
- 虚拟地址空间 + 页表(标志位) -- 访问内存时提供了更好的安全性, 页表记录权限, 页是否存在
段页式存储
- 建立虚拟地址 与 物理地址的联系, 使用数据结构记录(起始地址 + 偏移量)
1.内存分段
- 划分: 以进程大小划分 --> 需要多少给多少,段长度可动态变化(段是逻辑单位)
- 优缺点
- 优: 无内碎片 -- 用户需要多少给多少
- 缺: 有外碎片 -- 每个段的长度不固定, 可能会产生了多个不连续的小物理内存, 新程序使用不了 --> 解决(但慢): 进行内存交换, 将内存写进磁盘, 再重新装载到内存(接着连续的部分)
- 缺: 内存交换效率可能低 -- 分段容易产生外碎片 --> 内存不够(新程序用不了) --> 进行swap交换 (若内较存大则会十分慢)
- 物理地址与虚拟地址的联系 -- 段选择因子和段内偏移量
2.内存分页
- 划分: 将物理内存划分为固定的大小(4KB) -->为了合理利用空间(页是物理单位)
- 优缺点
- 优: 无外碎片 -- 都是以页为单位连续的去分配内存, 回收的时候时能用上的
- 优: 内存交换效率高 -- 页的大小比较小, swap交换也就快了
- 缺: 内碎片 -- 用户申请的内存不一定是以页的整数倍
- 物理地址与虚拟地址的联系 -- 多级页表
为什么需要多级页表? 假设32位下
- 4G的物理内存, 拆分位4KB的小内存, 那么一个进程的页表需要装载2^20个页表项, 一个页表项4字节--合4MB
- 进程的地址空间时独立的, 一个进程就需要大约4MB, 进程越多, 消耗就越大
- 所以使用多级页表 + 按需分配来节省空间
- 我们通常不会为一个内存分配很大的内存
- 多级页表 : 一个一级页表就能覆盖所有4G内存
- 按需分配 : 若一级页表的某个页表项没被用到, 就不需要创建二级页表, 需要再创建对应的二级页表就行, 这样就节省空间了
3.段页式
- 划分: 先根据进程大小分段, 再更具其进行分页
Swap机制
- 当内存满时, 会把一些内存交换至Swap空间
内存满了的时候还要其它机制
- 后台内存回收: 在物理内存紧张的时候,会唤醒 kswapd 内核线程来回收内存(异步)
- 直接内存回收: 后台回收太慢, 自己动手--该过程是同步的,会阻塞进程的执行
- OOM机制: 干掉占用内存最多的进程来缓解压力
对于内存的回收
- 文件页 : 内核缓存的磁盘数据和内核缓存的文件数据等
- 干净页: 没被修改的, 直接释放就行, 需要的时候再加载
- 脏页: 被修改了, 先写进磁盘, 再释放内存
- 匿名页: 一般是堆栈的数据, 没有对应的载体, 且下次还可能访问, 不能直接释放
- Swap交换: 先将数据写进磁盘中的Swap分区, 再释放内存, 下次访问再从磁盘加载