srs(state 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

    /****************************************************************/ 

  

 

 

 

posted @ 2024-02-01 17:44  yushimeng  阅读(37)  评论(0编辑  收藏  举报