1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
2.段与页
3.段页各自工作机制
问题:如何把 分段式内存管理 与 分页式内存管理 联系起来?
1)虚拟内存定义
【图解】
2)段页同时存在:段面向用户/页面向硬件
【图解】
用户程序分段存储在虚拟内存,包括数据段,代码段等;
以用户代码段为例:
把用户代码段分为3个内存页进行存储,其中某段的虚拟地址为0x00345008,映射的物理地址是 0x7008;
补充:
0)背景
1)段页同时存在时的重定位(重定位也称为地址翻译,逻辑地址翻译为物理地址)
【图解】
最后:操作系统把物理内存地址送入地址总线读写该内存地址上的数据;
【小结】段页同时存在时的重定位(地址翻译)
内存管理的核心就是内存分配,所以从程序放入内存,使用内存开始(使用内存就是取指执行);
1)具体措施
2)段页式内存下程序如何载入内存?
补充:虚拟内存是不存在的,是操作系统用程序实现的;
3)段页式内存下程序载入内存的示意图如下:
【图解】
虚拟地址段表结构(采用分区算法分割虚拟内存,如最佳适合分区算法,分区算法需要存储基址和长度):
段号 | 基址(虚拟地址) | 长度 | 保护 |
0 | Xxx | Xx | xx |
1 | 0x45000 | 60K | R/W |
页表结构:
逻辑页号 | 页框号(物理页) | 保护 |
Xx | xx | xx |
...... | ...... | ...... |
0x45 | 7 | R/W |
3.1)逻辑地址:
3.2)逻辑地址(如数据段 : 0x300)翻译为物理地址的步骤如下:
地址翻译(重定向)完成后,操作系统把物理地址送入地址总线以读写该地址上的数据;
【代码解说】
1)copy_mem代码(分配虚拟内存,建立段表):
- int copy_mem(int nr, task_struct *p)
- {
- unsigned long new_data_base;
- // new_data_base就是虚拟内存基址,实际上是对虚拟内存的分割;每个新进程的虚拟内存空间是64M;
- // 0号进程的虚拟内存空间是0~64M-1;1号进程的虚拟内存空间是 64M~2*64M-1;......
- new_data_base = nr * 0x4000000; // 64M * nr ,nr是第几个进程
- // 建立段表,为段表的代码段设置基址;p是pcb;进程切换时,段表跟着切换;
- set_base(p->ldt[1], new_data_base);
- // 建立段表,为段表的数据段设置基址;p是pcb;进程切换时,段表跟着切换;
- set_base(p->ldt[2], new_data_base);
- }
1.1)各进程的虚拟地址
每个进程占用 64M 的虚拟地址空间,互不重叠;
1)copy_mem 拷贝内存代码 (详细步骤)
- // 拷贝内存
- 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分别是父进程,新进程的虚拟地址基址;
- // 拷贝页表,修改虚拟地址,为新进程建立页表
- copy_page_tables(old_data_base, new_data_base, data_limit);
- }
-
- // 拷贝页表,修改虚拟地址,为新进程建立页表
- int copy_page_tables(unsigned long from, unsigned long to, long size)
- {
- // 位运算得到父进程的页目录号指针 from_dir ;
- from_dir = (unsigned long *) ( (from>>20) & 0xffc );
- // 位运算得到新(子)进程的页目录号指针 to_dir ;
- to_dir = (unsigned long *) ( (to >> 20) & 0xffc );
- // 拷贝的字节数
- size = (unsigned long) (size + 0x3fffff) >> 22;
- //
- for (; size-- > 0; from_dir++, to_dir++) {
- // from_page_table 是父进程页表基址
- from_page_table = (0xfffff000 & *from_dir);
- // 申请(分配)一个物理内存页
- to_page_table = get_free_page();
- // 把新的物理内存页基址赋值给 to_dir
- *to_dir = ( (unsigned long) to_page_table ) | 7 ;
- }
- }
2)from_page_table 与 to_page_table
【图解】
2)把from_dir 对应的页表内容 拷贝到 to_dir 对应的页表
【代码】父进程页表内容拷贝到子进程页表的代码如下:
- // 父进程页表内容拷贝到子进程页表的代码
- for(; nr-- >0; from_page_table++; to_page_table++)
- {
- // from_page_table 是父进程页表(基址)
- this_page = *from_page_table;
- this_page &= ~2; // 只读
- // 把父进程页表 from_page_table 内容拷贝给子进程页表 to_page_table
- // 即 父进程页表基址 与 子进程页表基址相同,两者指向了同一块物理内存
- *to_page_table = this_page;
- *from_page_table = this_page;
- this_page -= LOW_MEM;
- this_page >>= 12;
- // 当前页被共享了,所以该页的mem_map 累加;
- mem_map[this_page]++;
- }
说明:
【图解】
1)MMU:
2)进程1的逻辑地址 0x300 翻译为物理地址0x7300的步骤:
3)地址翻译完成后,进程1 fork出了进程2
其中,进程2 翻译逻辑地址 0x300 的步骤如下:
至此:父子进程通过不同的虚拟内存空间实现了相互分离,相互独立;
refer2 https://zh.m.wikipedia.org/zh-hans/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E5%8D%95%E5%85%83
内存管理单元(英语:memory management unit,缩写为MMU),有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU)。
它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)[1]、内存保护、中央处理器高速缓存的控制,在较为简单的计算机体系结构中,负责总线的仲裁以及存储体切换(bank switching,尤其是在8位的系统上)。