操作系统lab2实验总结——Part1
这是MIPS虚拟映射布局图,在接下来的实验中,我们需要特别注意的地址分别是kuseg和kseg0区,首先列出这两个区域的意义。
MIPS虚存映射布局
32位的MIPS CPU最大寻址空间为4GB(2^32字节),这4GB虚存空间被划分为四个部分:
kuseg (TLB-mapped cacheable user space, 0x00000000 - 0x7fffffff): 这一段是用户模式下可用的地址,大小为2G,也就是MIPS约定的用户内存空间。需要通过MMU进行虚拟地址到物理 地址的转换。
kseg0 (direct-mapped cached kernel space, 0x80000000 - 0x9fffffff): 这一段是内核地址,其内存虚存地址到物理内存地址的映射转换不通过MMU,使用时只需要将地址的最高位清零 (& 0x7fffffff), 这些地址就被转换为物理地址。也就是说,这段逻辑地址被连续地映射到物理内存的低端512M空间。对这段地址 的存取都会通过高速缓存(cached)。通常在没有MMU的系统中,这段空间用于存放大多数程序和数据。对于有 MMU 的系统,操作系统的内核会存放在这个区域。
kuseg中有三个大小为PDMAP(4MB)的区域,分别从0x7f400000开始是ENVS,PAGES和User VPT。需要注意的是PAGES和User VPT的关系:User VPT中存放了1024个页表,由于自映射的关系,这个页表里又包括了页目录的页表,每一个页表中又有1024个页表项。PAGES存放了48个页表,这48个页表映射的内容是16*1024个物理页框的结构体(struct Page)的物理内存。在lab2实验中,我们只用了boot_map_segment函数将PAGES的内容进行了映射,并没有操作User VPT的空间。
第一幅图将是我们理解整个mm/pmap.c的核心,接下来将按照init/init.c中的顺序进行逐一解释每个函数的细节和整体的启动布局。
1 //这是init/init.c里的函数调用顺序 2 mips_detect_memory(); 3 mips_vm_init(); 4 page_init();
init/init.c
一、mips_detect_memory()
1.代码块
1 void mips_detect_memory() 2 { 3 //in bytes 4 basemem = 64 * 1024 * 1024;//物理内存的大小为64MB-->26位 5 npage = 16 * 1024;//物理页框的个数 6 maxpa = basemem;//最大的物理地址 7 extmem = 0;//延申的空间 8 printf("Physical memory: %dK available, ", (int)(maxpa / 1024)); 9 printf("base = %dK, extended = %dK\n", (int)(basemem / 1024), 10 (int)(extmem / 1024)); 11 }
mips_detect_memory
2.初始化一些内存相关的参数,意义写在注释里,不赘述。
二、mips_vm_init()
1.代码块
1 void mips_vm_init() 2 { 3 extern char end[]; 4 extern int mCONTEXT; 5 extern struct Env *envs; 6 7 Pde *pgdir; 8 u_int n; 9 10 /* Step 1 */ 11 pgdir = alloc(BY2PG, BY2PG, 1); 12 printf("to memory %x for struct page directory.\n", freemem); 13 mCONTEXT = (int)pgdir;//mCONTEXT是虚拟地址 14 15 boot_pgdir = pgdir; 16 17 /* Step 2*/ 18 pages = (struct Page *)alloc(npage * sizeof(struct Page), BY2PG, 1); 19 printf("to memory %x for struct Pages.\n", freemem); 20 21 n = ROUND(npage * sizeof(struct Page), BY2PG); 22 23 boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R); 24 25 /* Step 3*/ 26 envs = (struct Env *)alloc(NENV * sizeof(struct Env), BY2PG, 1); 27 n = ROUND(NENV * sizeof(struct Env), BY2PG); 28 boot_map_segment(pgdir, UENVS, n, PADDR(envs), PTE_R); 29 30 printf("pmap.c:\t mips vm init success\n"); 31 }
View Code
2.目的:
step1:申请了4KB的页目录空间。(pgdir)
step2:申请了192KB的结构体空间,将这一块物理内存进行二级映射。
step3:申请了线程的空间。(这一块不在这里讨论)
3.布局图
在函数的起始位置声明了
extern char end[];
end[]的地址就是0x8040 0000,在tools/scse0_3.lds处进行了定义。
理解这个函数的关键是理解alloc()函数
1 static void *alloc(u_int n, u_int align, int clear) 2 { 3 4 extern char end[]; 5 u_long alloced_mem; 6 7 if (freemem == 0) 8 { 9 freemem = (u_long)end; 10 } 11 12 freemem = ROUND(freemem, align); 13 alloced_mem = freemem; 14 freemem = freemem + n; 15 16 if (clear) 17 { 18 bzero((void *)alloced_mem, n); 19 } 20 21 if (PADDR(freemem) >= maxpa) 22 { 23 panic("out of memorty\n"); 24 return (void *)-E_NO_MEM; 25 } 26 27 return (void *)alloced_mem; 28 }
alloc
freemem(全局变量)是end到physics Memory Max的空闲内存,每一次调用都将alloced_mem移到分配空间的开头,freemem移到分配空间的结尾,并返回了alloced_mem的地址。
所以mips_vm_init对于0x8040 0000以后的空间操作如下:
首先给pgdir在0x80400000申请了一个4KB的空间,然后给全局变量struct Page *pages申请了npage * sizeof(struct Page)即16*1024*12=192KB的空间。
1 struct Page{ 2 struct { 3 struct Page *le_next; 4 struct Page **le_prev; 5 } pp_link; 6 u_short pp_ref; 7 };
struct Page// sizeof(struct Page)=0xC
接着在boot_map_segment()里,每一次映射在一级页表的索引是相同的,所以会创造一个二级页表。
4.boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)
4.1代码块
1 void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm) 2 { 3 int i, va_temp; 4 Pte *pgtable_entry; 5 6 assert(size % BY2PG == 0); 7 8 for (i = 0; i < size; i += BY2PG) 9 { 10 pgtable_entry = boot_pgdir_Walk(pgdir, va + i, 1);//返回的是页表项的虚拟地址 11 *pgtable_entry = (pa + i) | perm | PTE_V; //物理地址映射上 12 } 13 }
View Code
4.2理解和画了一个的图(具体说明了end[]以后的虚拟空间是怎么布局的)
这个函数的目的是将虚拟地址空间[va, va+size) 和物理地址空间[pa, pa+size)建立映射关系。什么叫建立映射关系呢?即在虚拟地址所对应的二级页表里存放(物理页框的)物理地址。下面以下面这个调用为例,说明二级页表的映射过程。
boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);
这个函数的具体映射是通过boot_pgdit_walk实现的。
static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)
下面说明一下Pde*的理解(Pde*和Pte*是一样的)
Pde *pgdir_entryp;
在include/mmu.h中定义了
typedef u_long Pde;
Pde的类型就是unsigned long,占4B。它的指针是要寻找虚拟空间,大小也是一个页表项,4B。Pde和Pde*都是4B,使它们的转换非常方便。指针实际上一块以它为地址的内存单元。所以Pde的指针(Pde*)所指的地址就是虚拟地址,而这块地址的内容(Pde)是物理地址,所以实现了虚拟地址和物理地址的映射关系。
三、page_init()
1.代码块
1 void page_init(void) 2 { 3 /* Step 1 */ 4 LIST_INIT(&page_free_list); //page free list is a struct 5 /* Step 2*/ 6 freemem = ROUND(freemem, BY2PG); 7 /* Step 3 */ 8 int i; 9 int free_page_size = PADDR(freemem) / BY2PG; 10 for (i = 0; i < free_page_size; i++) 11 { 12 pages[i].pp_ref = 1; 13 } 14 /* Step 4 */ 15 for (; i < npage; i++) 16 { 17 pages[i].pp_ref = 0; 18 LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); 19 } 20 }
page_init()
2.目的
这个函数本身很简单,其中有两个问题需要注意:
- 问题1:struct Page *pages; 为什么要申请这些结构体?它和我们的页表有什么关系?
- 问题2:Step3"Mark all memory below `freemem` as used"的意义是什么?
在之前的操作中,我们一直使用了两个虚拟地址,一个是在kuseg0(end[](0x8040 0000) 以后),一个是在kuseg(里面有UPAGES,UVPT,UENVS)。前者不需要mmu直接将高位清零得到物理地址,后者需要mmu将虚拟地址映射到物理地址。所以其实我们对于end[]以后的所有操作可以理解成这是在对物理内存进行操作。物理内存的大小有多少呢?64MB。64MB的物理内存中有16*1024个物理页框,则16*1024个struct Page和64MB的物理页框是一一对应的。我们知道对空闲物理内存进行管理有两种基本的操作:1.位图;2.链表。所以page_free_list可以把空闲的struct Page串起来。
在step3中我们就将end[]后面已经使用的内核虚拟地址所对应的物理地址占用的内存,转换为 [PADDR(freemem) / BY2PG] 个页表,并把它们标记过已经使用的。
struct Page和物理内存是怎么一一对应的呢?
(pp - pages)<< PGSHIFT
就是算出这是pages里的第几个Page,再乘上4KB,就是物理地址了。
上面的两个问题就解决了。
转载于:https://www.cnblogs.com/puublog/p/10657916.html
操作系统lab2实验总结——Part1相关推荐
- python编写操作系统实验_6.828 操作系统 lab2 实验报告
Introduction 该 lab 主要需要编写操作系统的内存管理部分.内存管理分为两个部分: 内核的物理内存分配器 (physical memory allocator) 使得内核可以分配.释放内 ...
- ucore操作系统lab2实验报告
练习一.first-fit连续内存分配 文件:default_pmm.c (一).思路: 首先我们需要用一个数据结构来描述每个物理页(也称页帧),这里用了双向链表结构来表示每个页.链表头用 free_ ...
- 《操作系统》实验报告——熟悉Linux基础命令及进程管理
理论知识 Linux--进程管理 Linux--Linux C语言编程基础知识 手把手教你安装Linux虚拟机 一.实验目的 (1)加深对进程概念的理解,明确进程和程序的区别. (2)进一步认识并发执 ...
- 进程同步算法实现实验报告Linux,操作系统进程同步实验报告.doc
操作系统进程同步实验报告 实验三:进程同步实验 一.实验任务: (1)掌握操作系统的进程同步原理: (2)熟悉linux的进程同步原语: (3)设计程序,实现经典进程同步问题. 二.实验原理: (1) ...
- 计算机操作系统指导书,《计算机操作系统》实验指导书-2015
q.num++; System.out.println(\已生产第:\个产品!\ try { Thread.currentThread().sleep(100); } catch (Interrupt ...
- 操作系统期末实验:多用户二级文件系统
多用户二级文件系统 写在最前面 问题描述 要 求: 1 功能设计 1.1 系统层次结构 1.2初始化 1.2 子功能设计 2 源程序 2.1 系统实现主要的软件技术 2.2 数据结构 2.3 后端 2 ...
- CSAPP Lab2 实验记录 ---- Bomb Lab(Phase 1 - Phase 6详细解答 + Secret Phase彩蛋解析)
文章目录 Lab 总结博客链接 实验前提引子 实验需要指令及准备 Phase 1 Phase 2 Phase 3 Phase 4 Phase 5 Phase 6 Phase Secret(彩蛋Phas ...
- ZUCC_操作系统原理实验_Lab9进程的通信消息队列
lab9进程的通信–消息队列 一.两个进程并发执行,通过消息队列,分别进行消息的发送和接收 1.代码: //接受消息 #include<stdio.h> #include<stdli ...
- ZUCC_操作系统原理实验_实验九 消息队列
操作系统原理实验报告 课程名称 操作系统原理实验 实验项目名称 实验九 消息队列 实验目的 了解 Linux 系统的进程间通信机构 (IPC): 理解Linux 关于消息队列的概念: 掌握 Linux ...
最新文章
- Meteor:网络应用开发的瑞士军刀
- html元素一行显示不完收缩_这些常用的HTML标签,你还不知道吗?
- app vue 真机运行_uni-app黑魔法:小程序自定义组件运行到H5平台
- 【笔记】python os的使用 文件批量重命名 批量移动文件 将png转jpg代码
- bundlefusion论文阅读笔记
- php定义常量、判断有没有被定义、预定义常量、显示所有常量
- linux sshd启动失败 sshd re-exec requires execution with an absolute path
- JS window对象 Location对象 location用于获取或设置窗体的URL,并且可以用于解析URL。 语法: location.[属性|方法]...
- LeetCode 1832. 判断句子是否为全字母句
- mysql写下拉树_PHP+mysql实现从数据库获取下拉树功能的方法
- [导入]MSIL: call callvirt
- for循环、while循环、break跳出循环、continue结束本次循环、exit退出脚本
- c语言opengles程序,OpenGL教程 OpenGL编程指南
- 装机必备:微软常用运行库合集,2022版
- 如何用BabeLua运行Lua代码
- 【BCM】博通 linux-4.19 gcc-9.2 toolchain 环境搭建
- Python中str()函数的使用(学习笔记)
- linux下多点电容触摸屏实验
- Firewalld ip伪装和端口转发
- Common API Some/IP 环境的搭建