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开始执行。

uservec是U mode异常/中断的入口。

.globl uservec
uservec:
        csrrw a0, sscratch, a0 # 交换a0和sscratch

        # save the user registers in TRAPFRAME
        sd ra, 40(a0)
        sd sp, 48(a0)
		...
        sd t6, 280(a0)

	# save the user a0 in p->trapframe->a0
        csrr t0, sscratch
        sd t0, 112(a0)

        csrr t1, sepc
        sd t1, 24(a0) # BASE_ADDRESS 0x80400000

        ld sp, 8(a0) # kstack + PGSIZE
        ld tp, 32(a0)
        ld t1, 0(a0)
        # csrw satp, t1
        # sfence.vma zero, zero
        ld t0, 16(a0)
        jr t0

这里需要注意sscratch这个CSR寄存器的作用就是一个cache,它只负责存某一个值,这里它保存的就是trapframe结构体的位置。

实现批处理操作系统的细节

从Makefile中可以发现,scripts/pack.py,scripts/kernelld.py用来生成os/link_app.S和os/kernel_app.ld。link_app.S将用户程序加入kernel可执行文件中。kernel_app.ld规定了用户程序所在的段(.data.app[x])。在load_app()中将用户程序relocate到0x80400000。

需要relocate的原因:我们并不能直接跳转到 app_n_start 直接运行,因为用户程序在编译的时候,会假定程序处在虚存的特定位置,而由于我们还没有虚存机制,因此我们在运行之前还需要将用户程序加载到规定的物理内存位置。