和TLB有关的, 需要解决的问题有这么几个:
然后一个一个说…
TLB基本就这么多= =||`没啥东西…
This is the fun part…哈哈哈
这部分, 需要, 灰常严谨的逻辑和计算…`还要对page loading的步骤非常清晰. 任何一点对内存地址或者file offset的计算错误, 或者理解错误, 都会mess up.
所以, 咱们重新来列一遍从程序开始运行, 到page loading结束的整个过程:
本身, 整个过程还是很straight forward的. 可有一些special case需要handle…
多写两句和as有关的
很多人在刚开始写这个assignment的时候, 最先纠结的就是, as的结构要不要改...
这是个好问题, 但答案是: 不一定要改as, 但肯定需要+东西.
在解释为什么之前, 咱们先来看看dumb_vm的as里, 每个东西都是干嘛用的.
struct addrspace {
#if OPT_DUMBVM
vaddr_t as_vbase1;
paddr_t as_pbase1;
size_t as_npages1;
vaddr_t as_vbase2;
paddr_t as_pbase2;
size_t as_npages2;
paddr_t as_stackpbase;
#else
/* Put stuff here for your VM system */
#endif
};
dumb_vm里只保存两个segment/region的内存信息
然后咱们再研究下, 可能需要记录哪些信息. (并不一定全部都需要记录, 有些可以相互计算)
为了计算写入TLB的值, 需要
为了On-Demand Loading, 需要
对比一下上下两个列表, 我们缺点什么呢?
原来的as没有记录reg的permissions, 没有记录page/frame是不是被load过了, 没有分别记录每个page/frame的vaddr+paddr(当然, 可以通过seg的数据进行计算), 也没有每个reg对应的ELF信息...stack也是固定大小
所以原本的as是100%缺少东西的, 但这些额外的东西, 具体应该保存到哪里就自由发挥了.
补充1: OS161里假设了一个ELF程序只包括两个segment. 虽然实际可能不止2个, 但现阶段, 只支持2个也没问题.
更新1: 叫seg不太确切, 还是叫region好了. 另补充了一些需要记录的内容.
在dumbvm里, Physical Memory的分配机制非常简单. 在first和last之间, 一页一页的把内存分配出去, first逐次往后移动, 直到first = last, 就没有memory可用了. 也不回收, 不重复使用.
要想改变优化这个机制, 首先需要增加一个能够tracking所有可用Physical Frame使用状态的table. 在进行malloc或者free某个frame的时候, 在table中记录下来这个frame的状态. 这样, 如果存在free过的page, 这些page就可以优先被malloc, 从而重复利用已经free了的内存.
整体流程:
除此之外, 还需要修改allocation和free时候的操作, 让alloc能够通过某种algorithm来重复使用已经被free的内存位置. (用什么样的Algorithm就看各位了, 实在不行做liner search也是方法之一, 嘿嘿)
个别要点:
Instrumentation实在没什么好写的, 把declaration和initialization放对地方就好了.
]]>欢迎在评论中提问...
我会边写这个Note边添加Tips...
在OS161中, 所有应用程序在打开, 关闭, 读取, 写入一个文件的时候, 都是通过一个file descriptor [ID]来标识某一个文件的. 需要注意的是, file descriptor [ID]和vnode是完全两个东西.
每个thread都有单独一套file descriptors [ID], 但两个thread的两个不同的file descriptor [ID]如果标识的是同一个文件, 则共用一个vnode.
(这里所说的file descriptors实际上只是一个int, 是代表一个file descriptor的ID, 而实际上file descriptor要保存很多内容. 后边继续)
简单来说, 我们需要给每一个thread都增加一个, 记录所有opened file的机制. 我们暂称之为file table.
file table可以就是个简单的array, 但为了让file table的operation和thread以及syscall相互独立, 减少重复code并减少bug机会, 我强烈建议大家把file table相关的所有operations都写成独立的functions, 放在单独的文件里.
file table其实很简单, 一个array外加一个size_t记录array的长度, 就ok了. (这里说的所有array都是struct array, OS161中自带的kernel array lib)
file descriptor中需要记录以下几个东西,
然后就是操作file table的各种function, 比如
至此基本没有难点 (就类似写个小"class").
之后要把file table添加到thread里, 并且在thread_fork中进行initialization.
这是Assignment在"Implementing file system calls"中特别提到的一点.
在建立一个新的process时候, 不止要initialization一个file table, 而且还要把table的0, 1, 2分别设置成standard input, standard output和standard error, 不然printf就不work哦, 哈哈哈.
注: OS161中stdout和stderr是一样的.
open步骤 1. 创建file descriptor 2. vfs_open打开文件 (获取vnode) 3. 往file descriptor中存需要的数据 4. 把file descriptor加到file table中, 并取得ID 5. return ID给user program
如何用vfs_open打开文件, 请参考runprogram.c
有file descriptor就有vnode...如何用VOP_READ配合vnode和uio读文件, 请参看loadelf.c
注意offset怎么算!!!!!
考验你读code读够不够细致的时候到了...好好读vnode.h(的comment)吧.
Write和Read差在uio的配置, 和用VOP_WRITE.
请细读uio.h(的comment)= =...都是comment有用, code看不懂影响不是很大.
fork, getpid, waitpid, _exit...
注: 按我的理解, OS161所有的thread都是process, 只不过有parent和child之分.
这部分中, PID的管理逻辑是关键, 主要解决几个问题.
和file descriptor类似, PID也需要构建一个table. 区别在于, process table必须是global的! 也就是说, 整个系统只有一个process table.
然后考虑, 我们需要为每个PID/process保存哪些信息, 才能解决上边列举的几个问题.
process table相关的function (仅供参考),
第一个Assignment写完, 对于thread_sleep和thread_wakeup应该都很熟悉了.
两个function都consume一个pointer作为钥匙. 只要sleep的thread和wakeup的thread用的是同一个钥匙, 睡着的所有thread就能被唤醒.
这在implement waitpid和_exit的时候是很实用的.
这估计是这次Assignment中最难的部分.
fork的作用是生成一个和当前thread完全一样的thread. 说具体点就是,
fork system call要点:
thread_fork要点:
md_forkentry要点:
Follow这些要点, 应该可以比较顺利的搞定fork. 但我仍推荐先把fork和md_forkentry的工作原理搞明白, 再开始.
(如果写完了还没明白, 为什么要把md_forkentry pass给thread_fork, 那你绝对是奇葩)
到这里, A2中最难的部分过去了...
runprogram和execv, 我不确定有没有时间在下周一之前总结出来.
这两个大同小异, runprogram写好, execv只是稍微多点东西.
exception handling是打酱油...
大家加油
]]>