MIT 6.s081 Lab2 学习笔记

因为发现写笔记能促进自己学习,提出问题,所以记笔记

risk 5如何启动,其实是通过kernerl/kernel.ld跳到汇编文件kernel/entry.S 里面,

OUTPUT_ARCH( "riscv" )
ENTRY( _entry )

SECTIONS
{
  /*
   * ensure that entry.S / _entry is at 0x80000000,
   * where qemu's -kernel jumps.
   */
  . = 0x80000000;

  .text : {
    *(.text .text.*)
    . = ALIGN(0x1000);
    _trampoline = .;
    *(trampsec)
    . = ALIGN(0x1000);
    ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
    PROVIDE(etext = .);
  }

  .rodata : {
    . = ALIGN(16);
    *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
    . = ALIGN(16);
    *(.rodata .rodata.*)
  }

  .data : {
    . = ALIGN(16);
    *(.sdata .sdata.*) /* do not need to distinguish this from .data */
    . = ALIGN(16);
    *(.data .data.*)
  }

  .bss : {
    . = ALIGN(16);
    *(.sbss .sbss.*) /* do not need to distinguish this from .bss */
    . = ALIGN(16);
    *(.bss .bss.*)
  }

  PROVIDE(end = .);
}
~                                                                                                                                                                                    
~                                                                                                                                                                                    
~                                      

如何跳的,其实就是把entry.S文件放在0x8000000中,然后跳到那里执行汇编

     # qemu -kernel loads the kernel at 0x80000000
        # and causes each CPU to jump there.
        # kernel.ld causes the following code to
        # be placed at 0x80000000.
.section .text
_entry:
        # set up a stack for C.
        # stack0 is declared in start.c,
        # with a 4096-byte stack per CPU.
        # sp = stack0 + (hartid * 4096)
        la sp, stack0
        li a0, 1024*4
        csrr a1, mhartid
        addi a1, a1, 1
        mul a0, a0, a1
        add sp, sp, a0
        # jump to start() in start.c
        call start
spin:
        j spin

执行汇编的时候, la sp, stack0先声明一个栈,然后倒数第三行call start就跳转到start.c里面去执行,start.c又会跳到kernel/main.c里面去执行

#include "types.h" 
#include "param.h" 
#include "memlayout.h" 
#include "riscv.h" 
#include "defs.h" 
 
volatile static int started = 0; 
 
// start() jumps here in supervisor mode on all CPUs. 
void    
main() 
{ 
  if(cpuid() == 0){ 
    consoleinit(); 
    printfinit(); 
    printf("\n"); 
    printf("xv6 kernel is booting\n"); 
    printf("\n"); 
    kinit();         // physical page allocator 
    kvminit();       // create kernel page table 
    kvminithart();   // turn on paging 
    procinit();      // process table 
    trapinit();      // trap vectors 
    trapinithart();  // install kernel trap vector 
    plicinit();      // set up interrupt controller 
    plicinithart();  // ask PLIC for device interrupts 
    binit();         // buffer cache 
    iinit();         // inode cache 
    fileinit();      // file table 
    virtio_disk_init(); // emulated hard disk 
    userinit();      // first user process 
    __sync_synchronize(); 
    started = 1; 
  } else { 
    while(started == 0) 
      ; 
    __sync_synchronize(); 
    printf("hart %d starting\n", cpuid()); 
    kvminithart();    // turn on paging 
    trapinithart();   // install kernel trap vector 
    plicinithart();   // ask PLIC for device interrupts 
  }     
 
  scheduler();         
}
~                                                                                                                                                                                    
~                                                                                                                                                                                    
-- VISUAL --                              

在main里面会调用userinit创建第一个进程,这个进程会执行一个汇编小程序initcode.S

系统调用的流程initcode.S,先将SYS_exec放到a7寄存器,然后通过ecall,走进syscall.c 里面的代码,通过trap获得exec对应的系统调用号(调用号映射表在syscall.h里面,SYS_exec映射到数字7),然后在syscall.c里面执行相应函数

System call tracing

1. 在user.h里面添加

int trace(int)

2. 在usys.pl里面添加

entry("trace");

3.在syscall.h里面添加

#define SYS_trace 22

4.在kernel/sysproc.c里面添加

uint64
sys_trace(void)
{
  uint64 p;
  if(argaddr(0, &p) < 0)
    return -1;
  myproc()->mask = p; // trace的入参存到mask
  return 0;
}

5.在kernel/proc.h的proc结构体添加

int mask;

6.在kernel/proc.c的fork方法添加

  np->mask = p->mask;

作用是复制父进程的mask到子进程

7.修改syscall.c

extern uint64 sys_trace(void);

[SYS_trace]   sys_trace,

syscall方法里面添加

 if(1 << num & p->mask) {
                printf("%d: syscall: %s -> %d\n", p->pid, masks[num-1], p->trapframe->a0);
        }

其中masks的定义如下:

char* masks[23] = {"fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};

 总结来说,就是在用户态文件里面声明函数trace,在系统调用头文件syscall.h里面添加对应的系统调用号SYS_trace,并在系统进程里面添加对应的系统方法sys_trace,然后在系统调用程序syscall.c里面添加extern unit64 sys_trace()声明,并修改其中方法syscall()的逻辑。 而实际上trace的执行流程就是:

用户态调用trace,根据entry('trace')找到对应的调用号是SYS_trace, 然后在syscall里面根据调用号,找到系统方法sys_trace(),执行以后把用户设定的mask复制到proc结构体,然后。。。

Sysinfo

0. Makefile里面的UPROGS变量添加

$U/_sysinfotest\
$U/_sysinfo\

1. user.h里面添加:

struct sysinfo;
int sysinfo(struct sysinfo*);//这里我写的是struct sysinfotest(void)不知道为啥我错了额?
//不过看题目要求是这么写的

2. usys.pl里面添加:

entry("sysinfo");

 3.在syscall.h里面添加

#define SYS_sysinfo 23

4.在kernel/sysproc.c里面添加sys_sysinfo,参考sys_fstat(void),

uint64
sys_sysinfo(void)
{
  struct sysinfo info;
  uint64 addr; // user pointer to struct stat

  if(argaddr(0, &addr) < 0) // 不能是argaddr(1, &addr) 否则会出错
    return -1;
  struct proc *p = myproc();
  info.freemem = freemem_size();
  info.nproc = proc_num();

  if(copyout(p->pagetable, addr, (char*)&info, sizeof(info))) {
    return -1;
  }
  return 0;
}

5.在kalloc里面添加freemem_size用来计算剩余内存

int freemem_size() {

  struct run *r;

  int num = 0;
  r = keme.freelist;
  while(r) {
    num++;
    r = r->next;
  }

  return num * PGSIZE;

}

6.在proc.c里面添加proc_num计算进程数量

int proc_num(void) {
  struct proc *p;
  int num = 0;
  for(p = proc; p < &proc[NPROC]; p++) {
    if(p -> state != UNUSED) {
      num++;
    }
  }
  return num;
}

7. 在kernel/defs.h里面添加声明

int freemem_size(void);
int proc(void);

8. 在kernel/syscall.c里面添加声明

extern unint64 sysinfo(void);
[SYS_sysinfo]   sys_sysinfo,

9. 新建user/sysinfo.c文件,其实这个建不建都行,主要测试方便:

#include "kernel/types.h"
#include "user/user.h"
#include "kernel/sysinfo.h"

int main(int argc, char *argv[])
{
if (argc != 1)
{
        fprintf(2, "error input\n");
        exit(1);
}
struct sysinfo info;
sysinfo(&info);
printf("%d %d", info.freemem, info.nproc);
exit(0);
}

在debug的过程中,总结一下,还是那句话,要解决问题,得先了解问题,明白问题是什么,这在我找argaddr参数错误问题过程中起了蛮大作用

然后这个sysinfo,总体流程就是,我们首先通过用户态调用sysinfo,触发相应的系统调用sys_sysinfo,在调用里面实现获取内存空闲容量和当前进程数(这两个只有在内核态的时候才能获取),再把这些信息通过copyout,内存拷贝,拷贝到指定的用户态地址

至此lab 2,搞完了,撒花,哈哈哈

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值