文章目录
1.内存分段管理
1. 1.内存分段管理逻辑图
1. 2. 为什么要分段管理?
- 程序员眼中程序是由若干部分组成, 每个段有各自的特点和用途
- 比如代码段只读 数据段可写 如果放在一起那么都必须可写,所以在写数据的时候很容易污染代码段的代码。
- 栈段会动态增长 如果不分开管理 比如栈段一开始的空间不足 需要增加空间 那么就需要把所有整个程序都要移动到一个新的空间中,这样系统开销太大。
- 函数库 有时候可能不需要载入 分段管理 就可以控制整个函数库是否需要载入
1. 3. 为什么需要LTD?
- LTD中存储的每个段的基地址,通过基地址+逻辑地址确定物理地址。因为程序在编译的时候逻辑地址就已经确定了如果按照逻辑地址对于物理地址 那么一个内存就只能运行一个进程 因为每个程序的逻辑地址都是从0开始的。
1. 4. 分段管理有什么缺点?
- 内存利用效率低 为了充分利用段与段之间的内存间隙(内存碎片),所以就需要内存紧缩,移动一个段(复制内容) 内存紧缩需要花费大量时间。
1. 5.内存是怎样按照段分割呢?
- 会维护一张内存分配与使用情况的表 每次分配和使用都会更新这个表的情况
- 请求分配过程:
-
释放内存过程
-
再次申请过程
2.内存分页管理
2.1. 为什么要有内存分页管理
- 为了解决内存分段导致内存效率问题
2.2 是怎样解决这个问题的呢?
-
分段产生效率问题 是因为空闲碎片放不下一个段,那么我们就在碎片空间放更小的页 操作系统将内存打散为固定大小的页 每个页4KB 以页为单位进行内存分配
-
MMU 负责将将逻辑地址转为物理地址。
2.3 有什么问题呢?
- 页表占有空间大。每个进程都需要维护一张页表 如4G的空间 每个页4k大小 那么就需要100W个页表项 每个4表项如下 4个字节 那么每个进程就需要40M的空间 10进程就需要400M空间 。实际上有些页表项是用不到的,那么能不转为右边那种呢?不行 如果只存储使用到的页表项 。那么就查找某页的时候 每次都必须遍历整个页表 才能找到所需的页号。如果是左边那种连续的页号 就可以折半查找 快速定位到所需的页号。
2.4 为了解决页表占有空间大的问题
- 多级页表
- 将2^20页表项分为 1024个页目录号 每个页目录有1024个页表项目。并且只给使用到的目录映射。如程序 只使用到三个目录 那么就只给着三个目录创建完整子目录 如图就只占有4*4M=16M 远远小于40M.
2.5 多级页表有什么问题呢?
-
- 会增加内存的访问次数
- 怎么解决呢?
-
- 对用户程序不好管理, 用户程序更希望内存分段的 因为这样用户程序可以更好的使用内存, 而对于操作系统来说希望内存分页的 ,可以更好的使用内存 。所以怎么办呢?
- 内存段页管理
3 .内存段页管理
3.1 段页是怎样完成地址翻译的
- 程序地址映射到虚拟地址上,MMU根据映射的虚拟地址翻译查看段表 完成物理地址的转化 然后再读取对应的页
3.2 段页式内存下的程序是如何载入内存的?
-
- 分配虚拟内存 即建立段表
-
- 分配物理内存 建页表 这个区域被打散成几个页 分配到物理内存中 并且建立页表
3.3 源码分析
-
从 fork()开始 因为fork会建立复制进程 会给复制的进程分配空间
在 linux/kernel/fork.c 中 fork()->sys_fork->copy_process的路已经走过了 int copy_process(int nr, long ebp,...) { ... ... copy_mem(nr, p); } int copy_mem(int nr, task_struct *p) { unsigned long new_data_base; new_data_base=nr*0x4000000; //64M*nr nr: // 设置基址 :建立代码段基址 ,给进程在整个虚拟内存中分配64M的虚拟内存 把虚拟内存赋给 set_base(p->ldt[1],new_data_base); // 建立数据段基地址 只不过目前这个代码段和地址段是公用一套的虚拟地址 这种有点弱智 p是pcb set_base(p->ldt[2],new_data_base); }
-
由于虚拟地址不重叠 所以大家可以共用一套页表。如果重叠 就不能共用一套页表
-
接下来分配虚拟内存 建页页表
int copy_mem(int nr, task_struct *p) {
unsigned long old_data_base;
old_data_base=get_base(current->ldt[2]);
copy_page_tables(old_data_base,new_data_base,data_limit);
int copy_page_tables(unsigned long from,unsigned long to, long size){
// from 32位老的虚拟地址 from右移动22位*4 就是子进程的页目录号
from_dir=(unsigned long *)((from>>20)&0xffc);
to_dir=(unsigned long *)((to>>20)&0xffc);
size = (unsigned long)(size+0x3fffff)>>22;
// 子进程每页目录分配分配子进程的页表
for(; size-->0; from_dir++, to_dir++){
from_page_table=(0xfffff000&*from_dir);
to_page_table=get_free_page();
}
- 分配 实际物理页
- 子进程拷贝父亲进程实际物理页
4 .内存换入与换出
4.1 为什么需要内存的换入与换出
- 内存换出和换入是为了解决小内存处理大内存程序的问题,如果内存不够,就申请空闲页,调页并且修改页表,内存换出就是为了解决申请空闲页的问题。
4.2 内存换入
- 当请求的页不在物理内存上时 会换入
4.3 内存换出
- FIFO
- 换出最先换入到内存的页 但是不灵活
- LRU 页面置换 淘汰最近最少使用的页
4.4 LRU怎么实现
- 时间戳时间 代价大
- 使用过为1 没使用过为0