操作系统折腾记(二)
今天室友问了我一个有趣的问题。我们正在做 mit 的 xv6 2020 版实验,他写了一段代码:
1 |
|
不妨设这个用户程序叫做 vapa
吧,编译、执行,得到结果:
1 |
|
Child 和 Parent 的 bar
变量值不同,这符合我们的预期——fork
的时候子进程复制父进程的变量值,之后对变量的修改是独立的。但是问题是——为什么两个 bar
的地址相同呢?
刚做了(其实还没完成)mit xv6 lab3 的我立刻产生了一个猜想——这个地址只是虚拟地址,fork
的时候,子进程会复制父进程的页表,修改变量的时候,只会更改它的物理地址,而不会(也没有必要)修改虚拟地址。
猜想需要验证才能成为真理,我试图在 vapa.c
里调用 walkaddr
获取物理地址,但是遇到了一堆麻烦——也是,访问物理地址这种事情,怎么会允许用户态的程序干呢。看来只能写一个系统调用 getpa
了——getpa
获取一个虚拟地址,返回物理地址(当然是在调用它的进程的页表中找)。
做过 mit xv6 lab2,所以写系统调用也不是什么难事:
首先在 user/user.h、user/usys.pl、kernel/syscall.h、kernel/syscall.c 中参照其他系统调用补上
getpa
;然后在 kernel/sysproc.c 中实现一个
sys_getpa
,由于内核在 kernel/vm.c 中已经为我们实现好了虚拟地址转物理地址的底层代码walkaddr
,所以直接调用之:1
2
3
4
5
6
7
8uint64
sys_getpa(void)
{
uint64 va;
if(argaddr(0, &va) < 0)
return -1;
return walkaddr(myproc()->pagetable, va);
}
如果我没记错的话,现在已经万事俱备了,于是我们用 getpa
在 vapa.c
中把物理地址一并输出:
1 |
|
编译执行,得到结果:
1 |
|
哈哈!物理地址果然变了,猜想正确!
操作系统折腾记(二)
https://xyfjason.github.io/blog-main/2021/10/06/操作系统折腾记(二)/