<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # Swoole協程之旅-后篇 ?本篇我們開始深入PHP來分析Swoole協程的驅動部分,也就是C棧部分。 ?由于我們系統存在C棧和PHP棧兩部分,約定名字: * C協程 C棧管理部分, * PHP協程 PHP棧管理部分。 ?增加C棧是4.x協程最重要也是最關鍵的部分,之前的版本種種無法完美支持PHP語法也是由于沒有保存C棧信息。接下來我們將展開分析,C棧切換的支持最初我們是使用騰訊出品[libco](https://github.com/Tencent/libco "libco")來支持,但通過壓測會有內存讀寫錯誤而且開源社區很不活躍,有問題無法得到及時的反饋處理,所以,我們剝離的c++ boost庫的匯編部分,現在的協程C棧的驅動就是在這個基礎上做的。 ?先來一張簡單的系統架構圖。 ![Swoole4.x架構圖](https://wiki.swoole.com/static/uploads/wiki/201901/29/421430900750.png "Swoole4.x架構圖")可以發現,Swoole的角色是粘合在系統API和php ZendVM,給PHPer用戶深度接口編寫高性能的代碼;不僅如此,也支持給C++/C用戶開發使用,詳細請參考文檔[C++開發者如何使用Swoole](https://wiki.swoole.com/wiki/page/633.html "C++開發者如何使用Swoole")。 C部分的代碼主要分為幾個部分 1. 匯編ASM驅動 2. Conext 上下文封裝 3. Socket協程套接字封裝 4. PHP Stream系封裝,可以無縫協程化PHP相關函數 5. ZendVM結合層 Swoole底層系統層次更加分明,Socket將作為整個網絡驅動的基石,原來的版本中,每個客戶端都要基于異步回調的方式維護上下文,所以4.x版本較之前版本比較,無論是從項目的復雜程度,還是系統的穩定性,可以說都有一個質的飛躍。 代碼目錄層級 ~~~ $ tree swoole-src/src/coroutine/ swoole-src/src/coroutine/ ├── base.cc //C協程API,可回調PHP協程API ├── channel.cc //channel ├── context.cc //協程實現 基于ASM make_fcontext jump_fcontext ├── hook.cc //hook └── socket.cc //網絡操作協程封裝 swoole-src/swoole_coroutine.cc //ZendVM相關封裝,PHP協程API ~~~ 我們從用戶層到系統至上而下有 PHP協程API, C協程API, ASM協程API。其中Socket層是兼容系統API的網絡封裝。我們至下而上進行分析。 ASM x86-64架構為例,共有16個64位通用寄存器,各寄存器及用途如下 * %rax 通常用于存儲函數調用的返回結果,同時也用于乘法和除法指令中。在imul 指令中,兩個64位的乘法最多會產生128位的結果,需要 %rax 與 %rdx 共同存儲乘法結果,在div 指令中被除數是128 位的,同樣需要%rax 與 %rdx 共同存儲被除數。 * %rsp 是堆棧指針寄存器,通常會指向棧頂位置,堆棧的 pop 和push 操作就是通過改變 %rsp 的值即移動堆棧指針的位置來實現的。 * %rbp 是棧幀指針,用于標識當前棧幀的起始位置 * %rdi, %rsi, %rdx, %rcx,%r8, %r9 六個寄存器用于存儲函數調用時的6個參數 * %rbx,%r12,%r13,%14,%15 用作數據存儲,遵循被調用者使用規則 * %r10,%r11 用作數據存儲,遵循調用者使用規則 也就是說在進入匯編函數后,第一個參數值已經放到了 %rdi 寄存器中,第二個參數值已經放到了 %rsi 寄存器中,并且棧指針 %rsp 指向的位置即棧頂中存儲的是父函數的返回地址 x86-64使用swoole-src/thirdparty/boost/asm/make\_x86\_64\_sysv\_elf\_gas.S ~~~ //在當前棧頂創建一個上下文,用來執行執行第三個參數函數fn,返回初始化完成后的執行環境上下文 fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); make_fcontext: /* first arg of make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax /* reserve space for context-data on context-stack */ /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax /* third arg of make_fcontext() == address of context-function */ movq %rdx, 0x38(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ movq %rcx, 0x40(%rax) ret /* return pointer to context-data * 返回rax指向的棧底指針,作為context返回/ ~~~ ~~~ //將當前上下文(包括棧指針,PC程序計數器以及寄存器)保存至*ofc,從nfc恢復上下文并開始執行。 intptr_t jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); jump_fcontext: //保存當前寄存器,壓棧 pushq %rbp /* save RBP */ pushq %rbx /* save RBX */ pushq %r15 /* save R15 */ pushq %r14 /* save R14 */ pushq %r13 /* save R13 */ pushq %r12 /* save R12 */ /* prepare stack for FPU */ leaq -0x8(%rsp), %rsp /* test for flag preserve_fpu */ cmp $0, %rcx je 1f /* save MMX control- and status-word */ stmxcsr (%rsp) /* save x87 control-word */ fnstcw 0x4(%rsp) 1: /* store RSP (pointing to context-data) in RDI 保存當前棧頂到rdi 即:將當前棧頂指針保存到第一個參數%rdi ofc中*/ movq %rsp, (%rdi) /* restore RSP (pointing to context-data) from RSI 修改棧頂地址,為新協程的地址 ,rsi為第二個參數地址 */ movq %rsi, %rsp /* test for flag preserve_fpu */ cmp $0, %rcx je 2f /* restore MMX control- and status-word */ ldmxcsr (%rsp) /* restore x87 control-word */ fldcw 0x4(%rsp) 2: /* prepare stack for FPU */ leaq 0x8(%rsp), %rsp // 寄存器恢復 popq %r12 /* restrore R12 */ popq %r13 /* restrore R13 */ popq %r14 /* restrore R14 */ popq %r15 /* restrore R15 */ popq %rbx /* restrore RBX */ popq %rbp /* restrore RBP */ /* restore return-address 將返回地址放到 r8 寄存器中 */ popq %r8 /* use third arg as return-value after jump*/ movq %rdx, %rax /* use third arg as first arg in context function */ movq %rdx, %rdi /* indirect jump to context */ jmp *%r8 ~~~ context管理位于context.cc,是對ASM的封裝,提供兩個API ~~~ bool Context::SwapIn() bool Context::SwapOut() ~~~ 最終的協程API位于base.cc,最主要的API為 ~~~ //創建一個c棧協程,并提供一個執行入口函數,并進入函數開始執行上下文 //例如PHP棧的入口函數Coroutine::create(PHPCoroutine::create_func, (void*) &php_coro_args); long Coroutine::create(coroutine_func_t fn, void* args = nullptr); //從當前上下文中切出,并且調用鉤子函數 例如php棧切換函數 void PHPCoroutine::on_yield(void *arg) void Coroutine::yield() //從當前上下文中切入,并且調用鉤子函數 例如php棧切換函數 void PHPCoroutine::on_resume(void *arg) void Coroutine::resume() //C協程執行結束,并且調用鉤子函數 例如php棧清理 void PHPCoroutine::on_close(void *arg) void Coroutine::close() ~~~ 接下來是ZendVM的粘合層 位于swoole-src/swoole\_coroutine.cc ~~~ PHPCoroutine 供C協程或者底層接口調用 //PHP協程創建入口函數,參數為php函數 static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv); //C協程創建API static void create_func(void *arg); //C協程鉤子函數 上一部分base.cc的C協程會關聯到以下三個鉤子函數 static void on_yield(void *arg); static void on_resume(void *arg); static void on_close(void *arg); //PHP棧管理 static inline void vm_stack_init(void); static inline void vm_stack_destroy(void); static inline void save_vm_stack(php_coro_task *task); static inline void restore_vm_stack(php_coro_task *task); //輸出緩存管理相關 static inline void save_og(php_coro_task *task); static inline void restore_og(php_coro_task *task); ~~~ 有了以上基礎部分的建設,結合我們上一篇文章中PHP內核執行棧管理,就可以從C協程驅動PHP協程,實現C棧+PHP棧的雙棧的原生協程。 下一篇文章,我們將挑一個客戶端實現分析socket層,把協程和Swoole事件驅動結合來分析C協程以及PHP協程在底層網絡庫的應用和實踐。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看