MIT 6.s081 lab2.2–sysinfo
No.1 写在前面的话
有了trace仔细钻研的基础,sysinfo就显得简单了许多,不过,还是有一些小坑需要避免【我就踩到了】。
No.2 实验思路
在进行每次实验之前都应该仔细读要求。题目提及sysinfo收集有关正在运行的系统的信息,看一看user/sysinfotest.c:
int
main(int argc, char *argv[])
{
printf("sysinfotest: start\n");
testcall();
testmem();
testproc();
printf("sysinfotest: OK\n");
exit(0);
}
养成看一段代码前先看main的习惯,通过这里就可以简单知道,sysinfo会开始测试系统调用,内存和进程情况;简单了解后,代码就可以开始写了:
添加系统调用的步骤在此处略过,首先模仿所写trace,根据题目要求【系统调用采用一个参数:一个指向struct sysinfo
的指针】可写得:
uint64
sys_sysinfo(void){
struct sysinfo info;//指向struct sysinfo的指针
if(argaddr(0, &) < 0)//因为是指针,利用argaddr
return -1;
return 0;
}
接下来开始慢慢补充。
提示说,sysinfo
需要将一个struct sysinfo
复制回用户空间,给出了需要参阅的代码,我们看一看:
//sys_fstat()
uint64
sys_fstat(void)
{
struct file *f;
uint64 st; // user pointer to struct stat
if(argfd(0, 0, &f) < 0 || argaddr(1, &st) < 0)
return -1;
return filestat(f, st);
}
//filestat()
// Get metadata about file f.
// addr is a user virtual address, pointing to a struct stat.
int
filestat(struct file *f, uint64 addr)
{
struct proc *p = myproc();
struct stat st;
if(f->type == FD_INODE || f->type == FD_DEVICE){
ilock(f->ip);
stati(f->ip, &st);
iunlock(f->ip);
if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
return -1;
return 0;
}
return -1;
}
但是实际上有用的信息并不多,它只是告诉我们用户指针的st以及一个用户虚拟地址指向st,另外又进行了文件的判断,这么看并没有思路。但是我们注意到filestat()中的copyout,进行追踪,看到这条消息:
// Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error.
//将 len 个字节从 src 复制到给定页表中的虚拟地址 dstva
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
实际上,注释已经写的很明白,通过理解可得:要将一个struct sysinfo
复制回用户空间,可以利用copyout,将sysinfo所占的总字节逐个拷贝到虚拟地址【之后的页表课可以得知,在这里的虚拟地址就是用户空间】,那么根据filestat,模仿可得:
if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
return -1;
但是这里的addr还很迷糊,应该指向哪里,实际上,通过参看提示中的声明可得:
struct sysinfo;
int sysinfo(struct sysinfo *);
也就是说,用户调用sysinfo后我们会获得一个指针,指向sysinfo的地址;而sysinfo预先被我们声明在user.h中,这就表明,它的地址也就是用户态下的虚拟地址,那么,补充变量,用于存储地址:
uint64 addr;//其实我并不知道为什么用uint64,只是模仿定义
if(argaddr(0, &addr) < 0)
这样三分之一就已经完成,继续往下进行。
首先看一看kalloc.c:
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
//memset的作用是复制字符5到参数r【也即空闲链表】所指处的前pgsize个字符
//名副其实的填充垃圾……
//pgsize——每页字节数
return (void*)r;
}
那么现在就清楚了,要获取空闲内存量,就可以借助freelist进行,利用int存储遍历的结果,最后返回即可。当然,目前还不清楚锁,也还是模仿得:
//获取空闲内存量
int
GetMem(void)
{
struct run *r;
uint64 freebits = 0;
acquire(&kmem.lock);
r = kmem.freelist;
//因为要持续获得,得用while连续判断,一开始我用if
while(r){
++freebits;
kmem.freelist = r->next;
}
release(&kmem.lock);
return freebits * PGSIZE;
//一个freelist表示一【页】freemem
//我一开始没想到,只是返回freebits.
//但要知道空闲内存【量】,不应该只是知道有几页,还要知道每一页占多大
}
在这里,光是写出来还不够,因为重新添加了一个函数,我们得要和添加系统调用原型一样添加函数定义才行:
//defs.h
// kalloc.c
void* kalloc(void);
void kfree(void *);
void kinit(void);
int GetMem(void);
继续查看如何获取进程数,来到proc.c,根据题目要求,需要设置一个不为【UNUSED】的数,那么就得寻找使用示例。在这里,我先在最后定义了准备填写的函数,正要寻找怎么获取,没想到上面的procdump()就有我想获取的信息【太好了!】;我们只需看一看这里:
struct proc *p;
char *state;
printf("\n");
for(p = proc; p < &proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
很明显,这段for循环就是在遍历进程,如果是未使用则直接跳过,正好符合要求,模仿可得:
//获取进程数
int
GetProcNum(void){
struct proc *p;
uint64 count = 0;
for(p = proc; p < &proc[NPROC]; p++){
//目前为止还不知道锁的知识,但是觉得有必要加入
acquire(&p->lock);
if(p->state == UNUSED){
continue;
}
else{ ++count;}
release(&p->lock);
}
return count;
}
与kalloc类似,也需要填入函数声明,目前为止,代码已经几乎完成。
我们目前只是补充了函数,但并没有对其进行调用;根据要求:【内核应该填写这个结构的字段:freemem
字段应该设置为空闲内存的字节数,nproc
字段应该设置为state
字段不为UNUSED
的进程数。】那么现在,是时候补充了! 完整代码如下:
uint64
sys_sysinfo(void){
uint64 addr;
struct sysinfo info;//指向struct sysinfo的指针
struct proc *p = myproc();
if(argaddr(0, &addr) < 0)//先判断地址是否存在
return -1;
info.freemem = GetMem();
info.nproc = GetProcNum();
//将一个struct sysinfo复制回用户空间
if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
return -1;
//要先进行赋值才能返回用户空间
return 0;
}
注意!
最后运行时如果timeout,来到gradelib.py的419行,将timeout时间改久一些,再次运行就可成功。
用时半天。