xv6_chapter2 Operating system organization

2.6 Code: starting xv6, the first process and system call 启动代码: entry.S 为每个CPU设置堆栈,然后跳进start.c的start函数。 # 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 .global _entry _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 # CPUs 0~7 has hartid from 0~7 addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 # jump to start() in start....

2024-01-04 · 2 min

xv6_lab2 System calls

Trace 实现系统调用int trace(int mask);, 当调用mask中包含的系统调用号,打印出来。 在Makefile中增加trace用户程序的编译 UPROGS=\ ... $U/_trace 在user/user.h中增加函数声明 int trace(int); 在usys.pl中增加user space trace函数的入口。可以看到user space调用的系统调用, 是由这个脚本生成的函数。 以trace为例, 提供了trace函数的入口.global trace, 随后将定义在syscall.h中的SYS_trace编号存入寄存器a7, 通过ecall命令进入内核态。 sub entry { my $name = shift; print ".global $name\n"; print "${name}:\n"; print " li a7, SYS_${name}\n"; print " ecall\n"; print " ret\n"; } entry("trace"); 在syscall.c的syscall函数中通过获取a7寄存器中的编号,找到我们添加的系统调用函数,sys_trace。 函数具体实现如下: // 在proc结构体中增加mask成员 struct proc { //... int mask; } // 将user space传入的mask,传递给当前进程的mask变量 uint64 sys_trace(void) { if(argint(0, &myproc()->mask) < 0) return -1; return 0; } // 随后执行的系统调用number如果 (1 << num == mask), 则打印 syscall(void) { // ....

2024-01-04 · 1 min

xv6_chapter1 Operating system interfaces

XV6 实现的所有系统调用: int fork() //Create a process, return child’s PID. void exit(int status) //Terminate the current process; status reported to wait(). No return. int wait(int *status) //Wait for a child to exit; exit status in *status; returns child PID. int kill(int pid) //Terminate process PID. Returns 0, or -1 for error. int getpid() //Return the current process’s PID. int sleep(int n) //Pause for n clock ticks. int exec(char *file, char *argv[]) //Load a file and execute it with arguments; only returns if error....

2023-12-30 · 3 min

risc-v Lookup

CSRs 参考riscv-privileged-20211203.pdf sstatus SPP: 在进入S mode之前hart处于什么mode。 用户态trap进入S mode,SPP被设置为0,其他情况为1。 如果SPP为0,执行SRET后,返回U mode。如果SPP为1,执行SRET后,返回S mode。随后SPP置0。 SIE: 1开启/0关闭 S mode的所有中断 in S mode。 在U mode时,SIE被忽略,S mode 的中断都是打开的。 SPIE: 当trap进入S mode,SIE会置0禁止S mode所有中断,SIE的旧值会保存到SPIE中。 执行SRET后,SIE被设置为SPIE中之前保存的旧值,SPIE置1。 stvec Supervisor Trap Vector Base Address Register, 保存S mode异常/中断的跳转地址。 MODE为0,直接跳转到BASE;MODE为1,跳转到BASE + cause * 4。 在linux entry.S中的做法为直接设置stvec为handle_exception地址,地址的后两位肯定是4bytes对齐的,所以为00。跳转到handle_exception后,分开处理中断、系统调用、异常。根据异常cause再跳转到不同的异常处理函数。 SIP SIE Supervisor Interrupt pending/enable Registers SIP中断挂起(待处理的中断)和SIE中断使能。每一位代表的中断与scause中的为每个中断分配的异常码一致。 当sstatus.SIE=1, SIP[x]=1, SIE[x]=1,表示系统能够处理某个中断。 SIP中有的位是只读的,不能通过直接写0来清除: SEIP is read-only in sip, and is set and cleared by the execution environment, typically through a platform-specific interrupt controller....

2023-05-26 · 1 min

uCore_Chapter3 多道程序与分时多任务

分时多任务系统与抢占式调度 RISC-V架构中断 以内核所在的 S 特权级为例,中断屏蔽相应的 CSR 有 sstatus 和 sie 。sstatus 的 sie 为 S 特权级的中断使能,能够同时控制三种中断,如果将其清零则会将它们全部屏蔽。即使 sstatus.sie 置 1 ,还要看 sie 这个 CSR,它的三个字段 ssie/stie/seie 分别控制 S 特权级的软件中断、时钟中断和外部中断的中断使能。 当 Trap 发生时,sstatus.sie 会被保存在 sstatus.spie 字段中,同时 sstatus.sie 置零,这也就在 Trap 处理的过程中屏蔽了所有 S 特权级的中断; 当 Trap 处理完毕 sret 的时候, sstatus.sie 会恢复到 sstatus.spie 内的值。 也就是说,如果不去手动设置 sstatus CSR ,在只考虑 S 特权级中断的情况下,是不会出现 嵌套中断 (Nested Interrupt) 的。 嵌套中断可以分为两部分:在处理一个中断的过程中又被同特权级/高特权级中断所打断。默认情况下硬件会避免前一部分,也可以通过手动设置来允许前一部分的存在;而从上面介绍的规则可以知道,后一部分则是无论如何设置都不可避免的。 时钟中断与计时器 计数器保存在一个 64 位的 CSR mtime 另外一个 64 位的 CSR mtimecmp 的作用是:一旦计数器 mtime 的值超过了 mtimecmp,就会触发一次时钟中断。这使得我们可以方便的通过设置 mtimecmp 的值来决定下一次时钟中断何时触发。...

2023-05-17 · 2 min

uCore_Chapter1 应用程序与基本执行环境

Notes make build # 仅编译 make run # 编译+运行qemu make run LOG=trace # 其他选项可以看Makefile make clean # rm build/ make debug # 编译+运行gdb调试 每次在make run之前,尽量先执行make clean以删除缓存,特别是在切换ch分支之后。 退出 qemu 的方法 如果是正常推出,uCore 会自动关闭 qemu,但如果 os 跑飞了,我们不能通过 Ctrl + C 来推出。此时可以先按下 Ctrl+A ,再按下 X 来退出 Qemu。 ch1 流程 // entry.S _entry la sp, boot_stack_top //设置堆栈 call main // main.c clean_bss(); printf("hello wrold!\n"); consputc(); console_putchar(int c); sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); 怎么用gdb调试?`file kernel`然后? Makefile流程分析 根据make run 打印的信息:...

2023-04-25 · 1 min

uCore_Chapter2 批处理系统

make -C user clean # 在os目录,相当于cd user;make clean;cd .. make clean # 或者在user目录 git checkout ch2 make user BASE=1 CHAPTER=2 make run make test BASE=1 # make test 会完成 make user 和 make run 两个步骤(自动设置 CHAPTER) 流程 main(); printf("hello wrold!\n"); trap_init(); // 设置中断/异常处理地址 w_stvec((uint64)uservec & ~0x3); //把uservec地址传入,uservec在trampoline.S中定义 asm volatile("csrw stvec, %0" : : "r"(x)); // 设置stvec CSR loader_init(); run_next_app(); load_app(); usertrapret(trapframe, (uint64)boot_stack_top); w_sepc(trapframe->epc); r_sstatus(); w_sstatus(); userret(); sret // 返回到sepc中的值,0x80400000第一个app 应用程序系统调用ecall进入内核异常处理过程 // 进入应用程序 exit(MAGIC); syscall(SYS_exit, code); __syscall1(n, long(1234)); __asm_syscall("r"(a7), "0"(a0)); ecall //通过ecall 进入uservec uservec usertrap(); // ld t0, 16(a0) jr t0 r_scause(); csrr %0, scause // 应用层调用了ecall指令,所以scause自动被设置为8 syscall(); //在uservec中应用层传入eid到寄存器a7,这里读a7来判断是什么system call id = trapframe->a7; usertrapret(); // 这里回到第九行一样,循环,执行第二个应用程序 分析下uservec,注意:这里只是把stvec设置为uservec地址,并不会执行uservec下的代码,要等U mode的中断/异常到来时才会从uservec开始执行。...

2023-04-25 · 1 min