srs(state thread)如何实现协程切换
srs(state thread)如何实现协程切换? srs是单线程上运行的协程模型, 一个线程交替执行多个协程, 那么协程在用户空间是如何切换的呢?
首先关于thread.stack等内容可以自行阅读st thread代码, 这里只聊协程上下文切换过程.

417行的宏执行协程A上下文的保存
419行 _st_vp_schedule 在RUNQ中找到一个待执行协程B, 恢复协程B的上下文, 切换到该协程B执行.
协程B执行到io阻塞或者sleep事件, 就会重新把协程B缓存起来, 并寻找一个待执行协程(假设这里就AB两个协程),恢复协程A的上下文继续执行. 完成协程切换.
协程上下文保存
#define MD_SETJMP(env) _st_md_cxt_save(env)
/****************************************************************/
/*
* Internal __jmp_buf layout
*/
#define JB_RBX 0
#define JB_RBP 1
#define JB_R12 2
#define JB_R13 3
#define JB_R14 4
#define JB_R15 5
#define JB_RSP 6
#define JB_PC 7
.file "md.S"
.text
/* _st_md_cxt_save(__jmp_buf env) */
.globl _st_md_cxt_save
.type _st_md_cxt_save, @function
.align 16
_st_md_cxt_save:
/*
* Save registers.
* 寄存器数据的转存
*/
movq %rbx, (JB_RBX*8)(%rdi)
movq %rbp, (JB_RBP*8)(%rdi)
movq %r12, (JB_R12*8)(%rdi)
movq %r13, (JB_R13*8)(%rdi)
movq %r14, (JB_R14*8)(%rdi)
movq %r15, (JB_R15*8)(%rdi)
/* Save SP */
// 关于leaq和movq的区别:
// refs: https://courses.cs.washington.edu/courses/cse374/16wi/lectures/leaq-movq.pdf
// rsp寄存器存放的内容是栈顶的地址, leaq指令是把rsp+8的地址信息存放到(JB_RSP*8)(%rdi)中.
// 相当于是把栈pop之后的栈顶地址存放到了(JB_RSP*8)(%rdi).
leaq 8(%rsp), %rdx
movq %rdx, (JB_RSP*8)(%rdi)
/* Save PC we are returning to */
// rsp寄存器指向的栈顶地址上存放的是_st_md_cxt_save函数的返回地址,
// 把rsp放在(JB_PC*8)(%rdi)中, 等恢复协程的时候就可以jump到(JB_PC*8)(%rdi),相当于恢复的时候直接返回了.
// 注意现在只是把协程上下文保存了, 还没有切换呢.切回该协程的时候, 会jump到PC(下面_st_md_cxt_restore函数写明)
movq (%rsp), %rax
movq %rax, (JB_PC*8)(%rdi)
// 返回值为rax的异或, 恒为0;
// _st_md_cxt_save返回值什么时候为1呢?
// 在切回该协程的时候,jump到PC,PC就是_st_md_cxt_save返回的地方(上图417行), _st_md_cxt_restore根据输入的val设置了eax, eax作为函数返回值.
xorq %rax, %rax
ret
.size _st_md_cxt_save, .-_st_md_cxt_save
/****************************************************************/

协程上下文恢复
#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val)
/****************************************************************/
/* _st_md_cxt_restore(__jmp_buf env, int val) */
.globl _st_md_cxt_restore
.type _st_md_cxt_restore, @function
.align 16
_st_md_cxt_restore:
/*
* Restore registers.
*/
movq (JB_RBX*8)(%rdi), %rbx
movq (JB_RBP*8)(%rdi), %rbp
movq (JB_R12*8)(%rdi), %r12
movq (JB_R13*8)(%rdi), %r13
movq (JB_R14*8)(%rdi), %r14
movq (JB_R15*8)(%rdi), %r15
/* Set return value */
// 把val参数作为返回值. jump之后作为了_st_md_cxt_save的返回值.
test %esi, %esi
mov $01, %eax
cmove %eax, %esi
mov %esi, %eax
// 把栈顶寄存器rsp恢复为(之前_st_md_cxt_save中rsp栈pop之后的栈顶地址)
// 把PC指针恢复为_st_md_cxt_save的调用者, 并跳转到调用_st_md_cxt_save的地方
movq (JB_PC*8)(%rdi), %rdx
movq (JB_RSP*8)(%rdi), %rsp
/* Jump to saved PC */
jmpq *%rdx
.size _st_md_cxt_restore, .-_st_md_cxt_restore
/****************************************************************/

浙公网安备 33010602011771号