PA 操作系统的线程
批处理系统
riscv 指令集提供了ecall指令,当cpu执行到这个ecall指令后,会跳转到mtvec这个寄存器指向的地址执行操作系统的代码,我们常说的系统调用就是被编译成ecall指令,riscv架构是这样的,不同指令集有不同的"ecall"指令。
内核线程与用户线程
记录一下南京大学ics实验课的PA4 第一阶段的内容,有一点自己浅薄的理解,忘记的时候可以回来来看看
关于线程
从微观的角度说,线程本质上就是一小段代码,可以被独立执行。我们知道代码的执行关键需要栈(内存的一部分)和寄存器(在CPU上),一个个函数等于就是把这么多代码分开了,每一小片代码由一个函数名区分。在线程的运行过程中,数据都在栈和寄存器上,一个线程可以由栈和寄存器的状态来标识。那么怎么切换线程呢,很简单,就是把栈和寄存器上的数据"冻结"起来,这个操作我们有一个熟悉的名字,叫做上下文切换,这里的上下文指的是栈和寄存器。然后换成其他线程的,再执行其他线程的代码。怎么冻结起来?
当前线程 栈上 的数据好说,只要把栈指针指向内存中的其他地方就行了,这样新的线程就会使用这片新的内存区域,而不会影响之前线程栈上的数据了,那么CPU上的寄存器怎么冻结?,也很简单!就是把寄存器的值也压在栈上,这样就完美地保存了之前线程栈上与寄存器的数据。
所以当某个线程的执行被打断时,比如线程自己调用yield()
进行自陷,马上跳转操作系统中用于切换保存上下文的代码中,这段代码的地址可以是提前约定好的。在南大的实验中,这段代码如下,函数名为__am_asm_trap()
#define concat_temp(x, y) x ## y
#define concat(x, y) concat_temp(x, y)
#define MAP(c, f) c(f)
#if __riscv_xlen == 32
#define LOAD lw
#define STORE sw
#define XLEN 4
#else
#define LOAD ld
#define STORE sd
#define XLEN 8
#endif
#define REGS(f) \
f( 1) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8) f( 9) \
f(10) f(11) f(12) f(13) f(14) f(15) f(16) f(17) f(18) f(19) \
f(20) f(21) f(22) f(23) f(24) f(25) f(26) f(27) f(28) f(29) \
f(30) f(31)
#define PUSH(n) STORE concat(x, n), (n * XLEN)(sp);
#define POP(n) LOAD concat(x, n), (n * XLEN)(sp);
#define CONTEXT_SIZE ((32 3 1) * XLEN)
#define OFFSET_SP ( 2 * XLEN)
#define OFFSET_CAUSE (32 * XLEN)
#define OFFSET_STATUS (33 * XLEN)
#define OFFSET_EPC (34 * XLEN)
.align 3
.globl __am_asm_trap
__am_asm_trap:
#==========push the reigster to stack ================
addi sp, sp, -CONTEXT_SIZE
MAP(REGS, PUSH)
csrr t0, mcause
csrr t1, mstatus
csrr t2, mepc
STORE t0, OFFSET_CAUSE(sp)
STORE t1, OFFSET_STATUS(sp)
STORE t2, OFFSET_EPC(sp)
# set mstatus.MPRV to pass difftest
li a0, (1 << 17)
or t1, t1, a0
csrw mstatus, t1
#=======================================
#======== a0 is the parameter of __am_irq_handle ==========
mv a0, sp
#==========================================================
#========= save and switch the context =========
jal __am_irq_handle
#===============================================
#======== a0 is the return value of __am_irq_handle and ====
#====== is also the another thread stack pointer
mv sp, a0
#===========================================================
#=========== load another thread's context ===========
LOAD t1, OFFSET_STATUS(sp)
LOAD t2, OFFSET_EPC(sp)
csrw mstatus, t1
csrw mepc, t2
MAP(REGS, POP)
addi sp, sp, CONTEXT_SIZE
mret
#======================================================
用户线程和内核线程其实没有什么本质的区别,只不过他们的栈和代码段位于内存不同的地方而已。在南大的这个实验中,用PCB来描述一个用户线程或者内核线程。这个Context 指针其实就是栈顶指针。
#define STACK_SIZE (8 * PGSIZE)
typedef union {
uint8_t stack[STACK_SIZE] PG_ALIGN;
struct {
Context *cp;
AddrSpace as;
// we do not free memory, so use `max_brk' to determine when to call _map()
uintptr_t max_brk;
};
} PCB;
PCB的结构如下图
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhggjekf
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13