I. Virtual memory & File system
1. file可以直接映射进memory,更快,但无cache。
2. 从disk读取文件有两种方法:read()
and mmap()
- read():
- user process: 系统调用
read()
,trap into kernel - kernel: 执行interrupt handler,调用do_read();继而调用 file system的 read() 指令
- kernel space:读入的内容放到 kernel address space
- user space: kernel 将内容copy到 user address space
- user process: 系统调用
- mmap():
- user process:系统调用
mmap()
,trap into kernel - kernel :执行interrupt handler,调用 do_mmap();继而调用 file system 的 mmap() 指令
- user space: kernel 直接将 file 映射到 user address space中
- user process:系统调用
3. file system 中的 vnode 结构体:
- 对应一个电脑文件系统中的一个 file
- 其中还有一个 mmobj vn_mmobj 结构体: 是该file到address space 中的映射。 mmobj 包含:
+ mmo_operations
- lookuppage:根据[mmobj:page#], 找对应的 pframe
- fillpage:将 disk file 内容加载到 pframe
- dirtypage:将该 mmobj 指定的 pframe 设置为 dirty
- cleanpage:将该 mmobj指定的 pframe 清楚
- ref
- put
+ mmo_refcount
+ mmo_resident_pages
- vnode 为抽象的 inode,来实现 polymophism。也有与file 相关的 operations:
+ read
+ write
+ mmap(): 注意这个mmap 是vnode层面的function;**实际上返回的是 vnode->vn\_mmobj; 而这个vn\_mmobj则是通过上述的fillpage()实际将 disk 中的数据加载进 pframe**
II. vmmap.c
1. vmmap_lookup
1 | vmarea_t * vmmap_lookup(vmmap_t *map, uint32_t vfn) |
2. vmmap_map
1 | vmmap_map(vmmap_t *map, vnode_t *file, |
将指定的 file(anonymous/shadow) 映射map到该进程的“地址空间”address space中;经历一下步骤:
现在cur_proc的地址空间中开辟一块 vmarea
- if lopage=0: 开辟一个新的 vmarea
- if lopage!=0: 如果该位置已经被别的vmarea占用,那么删除就的vmarea
对刚获得的 vmarea 赋值:
- newvma->vma_start = lopage;
- newvma->vma_end = lopage + npages;
- newvma->vma_off = ADDR_TO_PN(off);
- newvma->vma_prot = prot;
- newvma->vma_flags = flags;
- list_link_init(&newvma->vma_plink);
- list_link_init(&newvma->vma_olink);
创建 mmobj(这是 vmarea 存在的意义)
- if file==NULL: mmobj为新创建的 anonymous obj;
mmobj = anon_create();
- if file!=NULL: mmobj为从vnode读入的 mmobj;
file->vn_ops->mmap(file, newvma, &mmobj);
- 判断是否需要创建 shadow obj
if MAP_PRIVATE & flags != 0
需要在 mmobj内部创建 shadow obj
- if file==NULL: mmobj为新创建的 anonymous obj;
将mmobj 链接到 vmarea上;
newvma->vma_obj = mmobj;
将vmarea 插入到 cur_proc 的 地址空间 map 中
3. vmmap_remove()
1 | int vmmap_remove(vmmap_t *map, uint32_t lopage, uint32_t npages) |
- 删除虚拟内存/virtual address 中的对应的 mmobj
- 并删除相应 page table entry,相当于释放物理内存
4. vmmap_is_range_empty
1 | int vmmap_is_range_empty(vmmap_t *map, uint32_t startvfn, uint32_t npages) |
判断指定的一段pages 在否在虚拟内存中未被占用
5. vmmap_read
1 | int vmmap_read(vmmap_t *map, const void *vaddr, void *buf, size_t count) |
从 cur_proc 当前地址空间map中指定的vaddr开始,读取“内存”中大小为count的内容到指定的 “内存”buf 中(因为这里的“内存”是虚拟内存,必须要借助pframe_lookup()读取physical memory的值)
- 循环执行以下步骤,直到读取字节数 达到count 数量;
- 注意:因为count 是字节数,而 address space 中以 page 为单位;并且page之间并非一直连续,是由 vmarea 链接而成;因此需要遍历 地址空间逐个vmarea、逐个page 读取
- 在 address space 中查找 start vaddr所属的 vmarea
- 获得该 vmarea 中所包含的 pages
- 循环读取所有 pages,memcpy到指定buf
- 调用
pframe_lookup()
找到 virtual page 的 physical page - 注意1:如果之前并没有 mapped physical address;那么会调用
pframe_get()
即刻map一个physical address - 注意2: 这里的 physical address 并非真正的,而依然是存在kernel address space 的 virtual address
- 调用
memcpy()
将对应的pframe中的内容copy到指定的buf中去 - 注意3:因为
memcpy()
是C语言库函数,这里实际上还是“virtual address”之间的内容拷贝。很有可能在memcpy()
实现了“virtual address to physical address”的转换
- 调用
- 直到该vmarea所有pages都copy完
- if count 未满足,则跳到下一个vmarea继续拷贝
- if count 满足,则退出