# Swoole協程之旅-中篇
?本篇我們開始深入PHP來分析Swoole協程的PHP部分。
?先從一個協程最簡單的例子入手:
~~~
<?php
go(function(){
echo "coro 1 start\n";
co::sleep(1);
echo "coro 1 exit";
});
echo "main flag\n";
go(function(){
echo "coro 2 start\n";
co::sleep(1);
echo "coro 2 exit\n";
});
echo "main end\n";
//輸出內容為
coro 1 start
main flag
coro 2 start
main end
coro 1 exit
coro 2 exit
~~~
可以發現,原生協程是在函數內部發生了跳轉,控制流從第4行跳轉到第7行,接著執行從第8行開始執行go函數,到第10行跳轉到了第13行,緊接著執行第9行,然后執行第15行的代碼。為什么Swoole的協程可以這樣執行呢?我們下面將一步一步進行分析。
? 我們知道PHP作為一門解釋型的語言,需要經過編譯為中間字節碼才可以執行,首先會經過詞法和語法分析,將腳本編譯為opcode數組,成為zend\_op\_array,然后經過vm引擎來執行。我們這里只關注vm執行部分。執行的部分需要關注幾個重要的數據結構
* Opcodes
~~~
struct _zend_op {
const void *handler;//每個opcode對應的c處理函數
znode_op op1;//操作數1
znode_op op2;//操作數2
znode_op result;//返回值
uint32_t extended_value;
uint32_t lineno;
zend_uchar opcode;//opcode指令
zend_uchar op1_type;//操作數1類型
zend_uchar op2_type;//操作數2類型
zend_uchar result_type;//返回值類型
};
~~~
從結構中很容易發現opcodes本質上是一個[三地址碼](https://zh.wikipedia.org/wiki/%E4%B8%89%E4%BD%8D%E5%9D%80%E7%A2%BC "三地址碼"),這里opcode是指令的類型,有兩個輸入的操作數數和一個表示輸出的操作數。每個指令可能全部或者部分使用這些操作數,比如加、減、乘、除等會用到全部三個;`!`操作只用到op1和result兩個;函數調用會涉及到是否有返回值等。
* Op arrays
`zend_op_array`PHP的主腳本會生成一個zend\_op\_array,每個function,eval,甚至是assert斷言一個表達式等都會生成一個新得op\_array。
~~~
struct _zend_op_array {
/* Common zend_function header here */
/* ... */
uint32_t last;//數組中opcode的數量
zend_op *opcodes;//opcode指令數組
int last_var;// CVs的數量
uint32_t T;//IS_TMP_VAR、IS_VAR的數量
zend_string **vars;//變量名數組
/* ... */
int last_literal;//字面量數量
zval *literals;//字面量數組 訪問時通過_zend_op_array->literals + 偏移量讀取
/* ... */
};
~~~
我們已經熟知php的函數內部有自己的單獨的作用域,這歸功于每個zend\_op\_array包含有當前作用域下所有的堆棧信息,函數之間的調用關系也是基于zend\_op\_array的切換來實現。
* PHP棧幀
PHP執行需要的所有狀態都保存在一個個通過鏈表結構關聯的VM棧里,每個棧默認會初始化為256K,Swoole可以單獨定制這個棧的大小(協程默認為8k),當棧容量不足的時候,會自動擴容,仍然以鏈表的關系關聯每個棧。在每次函數調用的時候,都會在VM Stack空間上申請一塊新的棧幀來容納當前作用域執行所需。棧幀結構的內存布局如下所示:
~~~
+----------------------------------------+
| zend_execute_data |
+----------------------------------------+
| VAR[0] = ARG[1] | arguments
| ... |
| VAR[num_args-1] = ARG[N] |
| VAR[num_args] = CV[num_args] | remaining CVs
| ... |
| VAR[last_var-1] = CV[last_var-1] |
| VAR[last_var] = TMP[0] | TMP/VARs
| ... |
| VAR[last_var+T-1] = TMP[T] |
| ARG[N+1] (extra_args) | extra arguments
| ... |
+----------------------------------------+
~~~
zend\_execute\_data 最后要介紹的一個結構,也是最重要的一個。
~~~
struct _zend_execute_data {
const zend_op *opline;//當前執行的opcode,初始化會zend_op_array起始
zend_execute_data *call;//
zval *return_value;//返回值
zend_function *func;//當前執行的函數(非函數調用時為空)
zval This;/* this + call_info + num_args */
zend_class_entry *called_scope;//當前call的類
zend_execute_data *prev_execute_data;
zend_array *symbol_table;//全局變量符號表
void **run_time_cache; /* cache op_array->run_time_cache */
zval *literals; /* cache op_array->literals */
};
~~~
`prev_execute_data`表示前一個棧幀結構,當前棧執行結束以后,會把當前執行指針(類比PC)指向這個棧幀。 PHP的執行流程正是將很多個zend\_op\_array依次裝載在棧幀上執行。這個過程可以分解為以下幾個步驟:
* **1:**為當前需要執行的op\_array從vm stack上申請當前棧幀,結構如上。初始化全局變量符號表,將全局指針EG(current\_execute\_data)指向新分配的zend\_execute\_data棧幀,EX(opline)指向op\_array起始位置。
* **2:**從`EX(opline)`開始調用各opcode的C處理handler(即\_zend\_op.handler),每執行完一條opcode將`EX(opline)++`繼續執行下一條,直到執行完全部opcode,遇到函數或者類成員方法調用:
* 從`EG(function_table)`中根據function\_name取出此function對應的zend\_op\_array,然后重復步驟1,將EG(current\_execute\_data)賦值給新結構的`prev_execute_data`,再將EG(current\_execute\_data)指向新的zend\_execute\_data棧幀,然后開始執行新棧幀,從位置`zend_execute_data.opline`開始執行,函數執行完將EG(current\_execute\_data)重新指向`EX(prev_execute_data)`,釋放分配的運行棧幀,執行位置回到函數執行結束的下一條opline。
* **3:**全部opcodes執行完成后將1分配的棧幀釋放,執行階段結束
* * *
有了以上php執行的細節,我們回到最初的例子,可以發現協程需要做的是,**改變原本php的運行方式,不是在函數運行結束切換棧幀,而是在函數執行當前op\_array中間任意時候(swoole內部控制為遇到IO等待),可以靈活切換到其他棧幀。**接下來我們將Zend VM和Swoole結合分析,如何創建協程棧,遇到IO切換,IO完成后棧恢復,以及協程退出時棧幀的銷毀等細節。 先介紹協程PHP部分的主要結構
* 協程 php\_coro\_task
~~~
struct php_coro_task
{
/* 只列出關鍵結構*/
/*...*/
zval *vm_stack_top;//棧頂
zval *vm_stack_end;//棧底
zend_vm_stack vm_stack;//當前協程棧指針
/*...*/
zend_execute_data *execute_data;//當前協程棧幀
/*...*/
php_coro_task *origin_task;//上一個協程棧幀,類比prev_execute_data的作用
};
~~~
協程切換主要是針對當前棧執行發生中斷時對上下文保存,和恢復。結合上面VM的執行流程我們可以知道上面幾個字段的作用。
* `execute_data`棧幀指針需要保存和恢復是毋容置疑的
* `vm_stack*`系列是什么作用呢?原因是PHP是動態語言,我們上面分析到,每次有新函數進入執行和退出的時候,都需要在全局stack上創建和釋放棧幀,所以需要正確保存和恢復對應的全局棧指針,才能保障每個協程棧幀得到釋放,不會導致內存泄漏的問題。(當以debug模式編譯PHP后,每次釋放都會檢查當全局棧是否合法)
* `origin_task`是當前協程執行結束后需要自動執行的前一個棧幀。
主要涉及到的操作有:
* 協程的創建`create`,在全局stack上為協程申請棧幀。
* 協程的創建是創建一個閉包函數,將函數(可以理解為需要執行的op\_array)當作一個參數傳入Swoole的內建函數go();
* 協程讓出,`yield`,遇到IO,保存當前棧幀的上下文信息
* 協程的恢復,`resume`,IO完成,恢復需要執行的協程上下文信息到yield讓出前的狀態
* 協程的退出,`exit`,協程op\_array全部執行完畢,釋放棧幀和swoole協程的相關數據。
經過上面的介紹大家應該對Swoole協程在運行過程中可以在函數內部實現跳轉有一個大概了解,回到最初我們例子結合上面php執行細節,我們能夠知道,該例子會生成3個op\_array,分別為 主腳本,協程1,協程2。我們可以利用一些工具打印出opcodes來直觀的觀察一下。通常我們會使用下面兩個工具
~~~
//Opcache, version >= PHP 7.1
php -d opcache.opt_debug_level=0x10000 test.php
//vld, 第三方擴展
php -d vld.active=1 test.php
~~~
我們用opcache來觀察沒有被優化前的opcodes,我們可以很清晰的看到這三組op\_array的詳細信息。
~~~
php -dopcache.enable_cli=1 -d opcache.opt_debug_level=0x10000 test.php
$_main: ; (lines=11, args=0, vars=0, tmps=4)
; (before optimizer)
; /path-to/test.php:2-6
L0 (2): INIT_FCALL 1 96 string("go")
L1 (2): T0 = DECLARE_LAMBDA_FUNCTION string("")
L2 (6): SEND_VAL T0 1
L3 (6): DO_ICALL
L4 (7): ECHO string("main flag
")
L5 (8): INIT_FCALL 1 96 string("go")
L6 (8): T2 = DECLARE_LAMBDA_FUNCTION string("")
L7 (12): SEND_VAL T2 1
L8 (12): DO_ICALL
L9 (13): ECHO string("main end
")
L10 (14): RETURN int(1)
{closure}: ; (lines=6, args=0, vars=0, tmps=1)
; (before optimizer)
; /path-to/test.php:2-6
L0 (9): ECHO string("coro 2 start
")
L1 (10): INIT_STATIC_METHOD_CALL 1 string("co") string("sleep")
L2 (10): SEND_VAL_EX int(1) 1
L3 (10): DO_FCALL//yiled from 當前op_array [coro 1] ; resume
L4 (11): ECHO string("coro 2 exit
")
L5 (12): RETURN null
{closure}: ; (lines=6, args=0, vars=0, tmps=1)
; (before optimizer)
; /path-to/test.php:2-6
L0 (3): ECHO string("coro 1 start
")
L1 (4): INIT_STATIC_METHOD_CALL 1 string("co") string("sleep")
L2 (4): SEND_VAL_EX int(1) 1
L3 (4): DO_FCALL//yiled from 當前op_array [coro 2];resume
L4 (5): ECHO string("coro 1 exit
")
L5 (6): RETURN null
coro 1 start
main flag
coro 2 start
main end
coro 1 exit
coro 2 exit
~~~
Swoole在執行`co::sleep()`的時候讓出當前控制權,跳轉到下一個op\_array,結合以上注釋,也就是在`DO_FCALL`的時候分別讓出和恢復協程執行棧,達到原生協程控制流跳轉的目的。
我們分析下`INIT_FCALL``DO_FCALL`指令在內核中如何執行。以便于更好理解函數調用棧切換的關系。
> VM內部指令會根據當前的操作數返回值等特殊化為一個c函數,我們這個例子中 有以下對應關系
>
> `INIT_FCALL`\=> ZEND\_INIT\_FCALL\_SPEC\_CONST\_HANDLER
>
> `DO_FCALL`\=> ZEND\_DO\_FCALL\_SPEC\_RETVAL\_UNUSED\_HANDLER
~~~
ZEND_INIT_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *fname = EX_CONSTANT(opline->op2);
zval *func;
zend_function *fbc;
zend_execute_data *call;
fbc = CACHED_PTR(Z_CACHE_SLOT_P(fname));
if (UNEXPECTED(fbc == NULL)) {
func = zend_hash_find(EG(function_table), Z_STR_P(fname));
if (UNEXPECTED(func == NULL)) {
SAVE_OPLINE();
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(fname));
HANDLE_EXCEPTION();
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(fname), fbc);
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
init_func_run_time_cache(&fbc->op_array);
}
}
call = zend_vm_stack_push_call_frame_ex(
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL, NULL); //從全局stack上申請當前函數的執行棧
call->prev_execute_data = EX(call); //將正在執行的棧賦值給將要執行函數棧的prev_execute_data,函數執行結束后恢復到此處
EX(call) = call; //將函數棧賦值到全局執行棧,即將要執行的函數棧
ZEND_VM_NEXT_OPCODE();
}
~~~
~~~
ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);//獲取到執行棧
zend_function *fbc = call->func;//當前函數
zend_object *object;
zval *ret;
SAVE_OPLINE();//有全局寄存器的時候 ((execute_data)->opline) = opline
EX(call) = call->prev_execute_data;//當前執行棧execute_data->call = EX(call)->prev_execute_data 函數執行結束后恢復到被調函數
/*...*/
LOAD_OPLINE();
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
ret = NULL;
if (0) {
ret = EX_VAR(opline->result.var);
ZVAL_NULL(ret);
}
call->prev_execute_data = execute_data;
i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
/*...*/
ret = 0 ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(call, ret);
} else {
zend_execute_internal(call, ret);
}
EG(current_execute_data) = execute_data;
zend_vm_stack_free_args(call);//釋放局部變量
if (!0) {
zval_ptr_dtor(ret);
}
} else { /* ZEND_OVERLOADED_FUNCTION */
/*...*/
}
fcall_end:
/*...*/
}
zend_vm_stack_free_call_frame(call);//釋放棧
if (UNEXPECTED(EG(exception) != NULL)) {
zend_rethrow_exception(execute_data);
HANDLE_EXCEPTION();
}
ZEND_VM_SET_OPCODE(opline + 1);
ZEND_VM_CONTINUE();
}
~~~
Swoole在PHP層可以按照以上方式來進行切換,至于執行過程中有IO等待發生,需要額外的技術來驅動,我們后續的文章將會介紹每個版本的驅動技術結合Swoole原有的事件模型,講述Swoole協程如何進化到現在。
- 序言
- 入門指引
- 環境依賴
- 編譯安裝
- 編譯參數
- 常見錯誤
- Cygwin
- Linux二進制包
- 快速起步
- 創建TCP服務器
- 創建UDP服務器
- 創建Web服務器
- 創建WebSocket服務器
- 設置定時器
- 執行異步任務
- 創建同步TCP客戶端
- 創建異步TCP客戶端
- 網絡通信協議設計
- 使用異步客戶端
- 多進程共享數據
- 使用協程客戶端
- 協程:并發 shell_exec
- 協程:Go + Chan + Defer
- 協程:實現 Go 語言風格的 defer
- 協程:實現 sync.WaitGroup 功能
- 編程須知
- sleep/usleep的影響
- exit/die函數的影響
- while循環的影響
- stat緩存清理
- mt_rand隨機數
- 進程隔離
- 版本更新記錄
- 4.3.1
- 4.3.0 [大版本]
- 4.2.13
- 4.2.12
- 4.2.11
- 4.2.10
- 4.2.9
- 4.2.8
- 4.2.7
- 4.2.0
- 4.1.0
- 4.0.1
- 4.0.0
- 向下不兼容改動
- 新特性使用
- 4.3.0 在 Process 中使用協程
- 4.3.0 延時事件機制改進
- 2.1.2 進程池模塊的使用
- 1.9.24 調度支持 Stream 模式
- 1.9.24 異步客戶端自動解析域名
- 1.9.17 支持異步安全重啟特性
- 1.9.14 使用異步客戶端超時機制
- 1.8.0 使用內置Http異步客戶端
- 1.7.16 使用迭代器遍歷Server所有連接
- 1.7.5 在Server中使用swoole_table
- 1.7.5 swoole_client支持sendfile接口
- 1.7.4 SSL隧道加密TCP-Server
- 1.7.4 task進程中使用毫秒定時器
- 1.7.3 固定包頭+包體協議自動分包
- 1.7.3 onTask直接return取代finish函數
- 1.7.2 swoole_process多進程模塊的使用
- 1.7.2 task進程使用消息隊列
- 項目路線圖
- php.ini選項
- 內核參數調整
- 開發者列表
- 衍生開源項目
- 框架
- 工具
- 分布式
- 通信協議
- 用戶與案例
- 物聯網項目
- 網絡游戲
- 騰訊(Tencent)
- 百度(Baidu.com)
- 閱文集團
- BiliBili(嗶哩嗶哩)
- 車輪互聯(chelun.com)
- (撈月狗) 游戲社區
- 儒博(roobo.com)
- 提交錯誤報告
- 常見問題
- 升級swoole版本的常見問題
- 生成可分發的二進制swoole版本
- 在phpinfo中有在php -m中沒有
- Connection refused是怎么回事
- Resource temporarily unavailable [11]
- Cannot assign requested address [99]
- swoole與node.js相比有哪些優勢
- swoole與golang相比有哪些優勢
- pcre.h: No such file or directory
- undefined symbol: __sync_bool_compare_and_swap_4
- 學習Swoole需要掌握哪些基礎知識
- 同步阻塞與異步非阻塞適用場景
- PHP7環境下出現zend_mm_heap corrupted
- Swoole 項目起源和名字由來
- '__builtin_saddl_overflow' was not declared in this scope
- Server
- 函數列表
- Server::__construct
- Server->set
- Server->on
- Server->addListener
- Server->addProcess
- Server->listen
- Server->start
- Server->reload
- Server->stop
- Server->shutdown
- Server->tick
- Server->after
- Server->defer
- Server->clearTimer
- Server->close
- Server->send
- Server->sendfile
- Server->sendto
- Server->sendwait
- Server->sendMessage
- Server->exist
- Server->pause
- Server->resume
- Server->getClientInfo
- Server->getClientList
- Server->bind
- Server->stats
- Server->task
- Server->taskwait
- Server->taskWaitMulti
- Server->taskCo
- Server->finish
- Server->heartbeat
- Server->getLastError
- Server->getSocket
- Server->protect
- Server->confirm
- 屬性列表
- Server::$setting
- Server::$master_pid
- Server::$manager_pid
- Server::$worker_id
- Server::$worker_pid
- Server::$taskworker
- Server::$connections
- Server::$ports
- 配置選項
- reactor_num
- worker_num
- max_request
- max_conn (max_connection)
- task_worker_num
- task_ipc_mode
- task_max_request
- task_tmpdir
- dispatch_mode
- dispatch_func
- message_queue_key
- daemonize
- backlog
- log_file
- log_level
- heartbeat_check_interval
- heartbeat_idle_time
- open_eof_check
- open_eof_split
- package_eof
- open_length_check
- package_length_type
- package_length_func
- package_max_length
- open_cpu_affinity
- cpu_affinity_ignore
- open_tcp_nodelay
- tcp_defer_accept
- ssl_cert_file
- ssl_method
- ssl_ciphers
- user
- group
- chroot
- pid_file
- pipe_buffer_size
- buffer_output_size
- socket_buffer_size
- enable_unsafe_event
- discard_timeout_request
- enable_reuse_port
- enable_delay_receive
- open_http_protocol
- open_http2_protocol
- open_websocket_protocol
- open_mqtt_protocol
- open_websocket_close_frame
- reload_async
- tcp_fastopen
- request_slowlog_file
- enable_coroutine
- max_coroutine
- task_enable_coroutine
- ssl_verify_peer
- 監聽端口
- 可選參數
- 可選回調
- 連接迭代器
- 預定義常量
- 事件回調函數
- onStart
- onShutdown
- onWorkerStart
- onWorkerStop
- onWorkerExit
- onConnect
- onReceive
- onPacket
- onClose
- onBufferFull
- onBufferEmpty
- onTask
- onFinish
- onPipeMessage
- onWorkerError
- onManagerStart
- onManagerStop
- 高級特性
- 改變Worker進程的用戶/組
- 回調函數中的 reactor_id 和 fd
- Length_Check 和 EOF_Check 的使用
- Worker與Reactor通信模式
- TCP-Keepalive死連接檢測
- TCP服務器心跳維持方案
- 多端口監聽的使用
- 捕獲Server運行期致命錯誤
- Server內存管理機制
- Server的兩種運行模式介紹
- Server中對象的4層生命周期
- 在worker進程內監聽一個Server端口
- 在php-fpm/apache中使用task功能
- 常見問題
- 為什么不要send完后立即close
- 如何在回調函數中訪問外部的變量
- 是否可以共用1個redis或mysql連接
- 關于onConnect/onReceive/onClose順序
- 4種PHP回調函數風格
- 不同的Server程序實例間如何通信
- 錯誤信息:ERROR (9006)
- eventLoop has already been created. unable to create swoole_server
- 壓力測試
- 并發10萬TCP連接的測試
- PHP7+Swoole/Nginx/Golang性能對比
- 全球Web框架權威性能測試 Techempower Web Framework Benchmarks
- Coroutine
- Coroutine
- Coroutine::set
- Coroutine::stats
- Coroutine::create
- Coroutine::exist
- Coroutine::getCid
- Coroutine::getPcid
- Coroutine::getContext
- Coroutine::defer
- Coroutine::list
- Coroutine::getBackTrace
- Coroutine::yield
- Coroutine::resume
- Coroutine::fread
- Coroutine::fgets
- Coroutine::fwrite
- Coroutine::sleep
- Coroutine::gethostbyname
- Coroutine::getaddrinfo
- Coroutine::exec
- Coroutine::readFile
- Coroutine::writeFile
- Coroutine::statvfs
- Coroutine\Channel
- Coroutine\Channel->__construct
- Coroutine\Channel->push
- Coroutine\Channel->pop
- Coroutine\Channel->stats
- Coroutine\Channel->close
- Coroutine\Channel->length
- Coroutine\Channel->isEmpty
- Coroutine\Channel->isFull
- Coroutine\Channel->$capacity
- Coroutine\Channel->$errCode
- Coroutine\Client
- Coroutine\Client->connect
- Coroutine\Client->send
- Coroutine\Client->recv
- Coroutine\Client->close
- Coroutine\Client->peek
- Coroutine\Http\Client
- 屬性列表
- Coroutine\Http\Client->get
- Coroutine\Http\Client->post
- Coroutine\Http\Client->upgrade
- Coroutine\Http\Client->push
- Coroutine\Http\Client->recv
- Coroutine\Http\Client->addFile
- Coroutine\Http\Client->addData
- Coroutine\Http\Client->download
- Coroutine\Http2\Client
- Coroutine\Http2\Client->__construct
- Coroutine\Http2\Client->set
- Coroutine\Http2\Client->connect
- Coroutine\Http2\Client->send
- Coroutine\Http2\Client->write
- Coroutine\Http2\Client->recv
- Coroutine\Http2\Client->close
- Coroutine\Redis
- Coroutine\Redis::__construct
- Coroutine\Redis::setOptions
- 屬性列表
- 事務模式
- 訂閱模式
- Coroutine\Socket
- Coroutine\Socket::__construct
- Coroutine\Socket->bind
- Coroutine\Socket->listen
- Coroutine\Socket->accept
- Coroutine\Socket->connect
- Coroutine\Socket->send
- Coroutine\Socket->sendAll
- Coroutine\Socket->recv
- Coroutine\Socket->recvAll
- Coroutine\Socket->sendto
- Coroutine\Socket->recvfrom
- Coroutine\Socket->getsockname
- Coroutine\Socket->getpeername
- Coroutine\Socket->close
- Coroutine\MySQL
- 屬性列表
- Coroutine\MySQL->connect
- Coroutine\MySQL->query
- Coroutine\MySQL->prepare
- Coroutine\MySQL->escape
- Coroutine\MySQL->begin
- Coroutine\MySQL->commit
- Coroutine\MySQL->rollback
- Coroutine\MySQL\Statement->execute
- Coroutine\MySQL\Statement->fetch
- Coroutine\MySQL\Statement->fetchAll
- Coroutine\MySQL\Statement->nextResult
- Coroutine\PostgreSQL
- Coroutine\PostgreSQL->connect
- Coroutine\PostgreSQL->query
- Coroutine\PostgreSQL->fetchAll
- Coroutine\PostgreSQL->affectedRows
- Coroutine\PostgreSQL->numRows
- Coroutine\PostgreSQL->fetchObject
- Coroutine\PostgreSQL->fetchAssoc
- Coroutine\PostgreSQL->fetchArray
- Coroutine\PostgreSQL->fetchRow
- Coroutine\PostgreSQL->metaData
- Coroutine\PostgreSQL->prepare
- Server
- 并發調用
- setDefer 機制
- 子協程+通道
- 實現原理
- 協程與線程
- 發送數據協程調度
- 協程內存開銷
- 4.0 協程實現原理
- 協程客戶端超時規則
- 協程執行流程
- 常見問題
- 運行中出現 Fatal error: Maximum function nesting level of '1000' reached, aborting!
- 為什么只能在回調函數中使用協程客戶端
- 支持協程的回調方法列表
- 錯誤信息: XXXX client has already been bound to another coroutine
- Swoole4 協程與 PHP 的 Yield/Generator 協程有什么區別
- Swoole4 協程與 Go 協程有哪些區別
- 編程須知
- 在多個協程間共用同一個協程客戶端
- 禁止使用協程 API 的場景(2.x 版本)
- 使用類靜態變量/全局變量保存上下文
- 退出協程
- 異常處理
- 擴展組件
- MongoDB
- 編程調試
- Runtime
- 文件操作
- 睡眠函數
- 開關選項
- 嚴格模式
- Timer
- swoole_timer_tick
- swoole_timer_after
- swoole_timer_clear
- Memory
- Lock
- swoole_lock->__construct
- swoole_lock->lock
- swoole_lock->trylock
- swoole_lock->unlock
- swoole_lock->lock_read
- swoole_lock->trylock_read
- swoole_lock->lockwait
- Buffer
- swoole_buffer->__construct
- swoole_buffer->append
- swoole_buffer->substr
- swoole_buffer->clear
- swoole_buffer->expand
- swoole_buffer->write
- swoole_buffer->read
- swoole_buffer->recycle
- Table
- Table->__construct
- Table->column
- Table->create
- Table->set
- Table->incr
- Table->decr
- Table->get
- Table->exist
- Table->count
- Table->del
- Atomic
- swoole_atomic->__construct
- swoole_atomic->add
- swoole_atomic->sub
- swoole_atomic->get
- swoole_atomic->set
- swoole_atomic->cmpset
- swoole_atomic->wait
- swoole_atomic->wakeup
- mmap
- swoole_mmap::open
- Channel
- Channel->__construct
- Channel->push
- Channel->pop
- Channel->stats
- Serialize
- swoole_serialize::pack
- swoole_serialize::unpack
- Http\Server
- Http\Server
- Http\Server->on
- Http\Server->start
- Http\Request
- Http\Request->$header
- Http\Request->$server
- Http\Request->$get
- Http\Request->$post
- Http\Request->$cookie
- Http\Request->$files
- Http\Request->rawContent
- Http\Request->getData
- Http\Response
- Http\Response->header
- Http\Response->cookie
- Http\Response->status
- Http\Response->gzip
- Http\Response->redirect
- Http\Response->write
- Http\Response->sendfile
- Http\Response->end
- Http\Response->detach
- Http\Response::create
- 配置選項
- upload_tmp_dir
- http_parse_post
- document_root
- http_compression
- 常見問題
- CURL發送POST請求服務器端超時
- 使用Chrome訪問服務器會產生2次請求
- GET/POST請求的最大尺寸
- WebSocket\Server
- 回調函數
- onHandShake
- onOpen
- onMessage
- 函數列表
- WebSocket\Server->push
- WebSocket\Server->exist
- WebSocket\Server::pack
- WebSocket\Server::unpack
- WebSocket\Server->disconnect
- WebSocket\Server->isEstablished
- 預定義常量
- 常見問題
- 配置選項
- WebSocket\Frame
- Redis\Server
- 方法
- Redis\Server->setHandler
- Redis\Server::format
- 常量
- Process
- Process::__construct
- Process->start
- Process->name
- Process->exec
- Process->write
- Process->read
- Process->setTimeout
- Process->setBlocking
- Process->useQueue
- Process->statQueue
- Process->freeQueue
- Process->push
- Process->pop
- Process->close
- Process->exit
- Process::kill
- Process::wait
- Process::daemon
- Process::signal
- Process::alarm
- Process::setAffinity
- Process::exportSocket
- Process\Pool
- Process\Pool::__construct
- Process\Pool->on
- Process\Pool->listen
- Process\Pool->write
- Process\Pool->start
- Process\Pool->getProcess
- Client
- 方法列表
- swoole_client::__construct
- swoole_client->set
- swoole_client->on
- swoole_client->connect
- swoole_client->isConnected
- swoole_client->getSocket
- swoole_client->getSockName
- swoole_client->getPeerName
- swoole_client->getPeerCert
- swoole_client->send
- swoole_client->sendto
- swoole_client->sendfile
- swoole_client->recv
- swoole_client->close
- swoole_client->sleep
- swoole_client->wakeup
- swoole_client->enableSSL
- 回調函數
- onConnect
- onError
- onReceive
- onClose
- onBufferFull
- onBufferEmpty
- 屬性列表
- swoole_client->errCode
- swoole_client->sock
- swoole_client->reuse
- 并行
- swoole_client_select
- TCP客戶端異步連接
- SWOOLE_KEEP建立TCP長連接
- 常量
- 配置選項
- ssl_verify_peer
- ssl_host_name
- ssl_cafile
- ssl_capath
- package_length_func
- http_proxy_host
- 常見問題
- Event
- swoole_event_add
- swoole_event_set
- swoole_event_isset
- swoole_event_write
- swoole_event_del
- swoole_event_exit
- swoole_event_defer
- swoole_event_cycle
- swoole_event_wait
- swoole_event_dispatch
- 常見問題
- epoll_wait 偶爾會用很長時間
- 異步回調
- 異步文件系統IO
- swoole_async_readfile
- swoole_async_writefile
- swoole_async_read
- swoole_async_write
- swoole_async_dns_lookup
- swoole_async::exec
- 異步MySQL客戶端
- swoole_mysql->__construct
- swoole_mysql->on
- swoole_mysql->connect
- swoole_mysql->escape
- swoole_mysql->query
- swoole_mysql->begin
- swoole_mysql->commit
- swoole_mysql->rollback
- swoole_mysql->close
- 異步Redis客戶端
- swoole_redis->__construct
- swoole_redis->on
- swoole_redis->connect
- swoole_redis->__call
- swoole_redis->close
- 異步Http/WebSocket客戶端
- swoole_http_client->__construct
- swoole_http_client->set
- swoole_http_client->setMethod
- swoole_http_client->setHeaders
- swoole_http_client->setCookies
- swoole_http_client->setData
- swoole_http_client->addFile
- swoole_http_client->get
- swoole_http_client->post
- swoole_http_client->upgrade
- swoole_http_client->push
- swoole_http_client->execute
- swoole_http_client->download
- swoole_http_client->close
- 異步Http2.0客戶端
- swoole_http2_client->__construct
- swoole_http2_client->get
- swoole_http2_client->post
- swoole_http2_client->setHeaders
- swoole_http2_client->setCookies
- 高級
- Swoole的實現
- Reactor線程
- Manager進程
- Worker進程
- Reactor、Worker、TaskWorker的關系
- Task/Finish特性的用途
- 在php-fpm或apache中使用swoole
- Swoole異步與同步的選擇
- TCP/UDP壓測工具
- swoole服務器如何做到無人值守100%可用
- MySQL的連接池、異步、斷線重連
- PHP中哪些函數是同步阻塞的
- 守護進程程序常用數據結構
- 隊列(Queue)
- 堆(Heap)
- 定長數組(SplFixedArray)
- 使用jemalloc優化swoole內存分配性能
- C開發者如何使用Swoole
- C++開發者如何使用Swoole
- 使用systemd管理swoole服務
- 網卡中斷設置
- 將Swoole靜態編譯內嵌到PHP
- 異步回調程序內存管理
- 日志等級控制
- 使用 asan 內存檢測
- Windows編譯
- Swoole協程之旅-前篇
- Swoole協程之旅-中篇
- Swoole協程之旅-后篇
- 協程CPU密集場景調度實現
- 其他
- 函數列表
- swoole_set_process_name
- swoole_version
- swoole_strerror
- swoole_errno
- swoole_get_local_ip
- swoole_clear_dns_cache
- swoole_get_local_mac
- swoole_cpu_num
- swoole_last_error
- Swoole社區
- Swoole技術會議
- 工作組(Working Groups)
- 參與開源項目指引
- 捐贈Swoole項目
- 加入Swoole開發組
- 非協程特性獨立擴展 (swoole_async)
- 附錄:Linux信號列表
- 附錄:Linux錯誤碼(errno)列表
- 附錄:Swoole錯誤碼列表
- 附錄:TCP連接的狀態
- 附錄:tcpdump抓包工具的使用
- 附錄:strace工具的使用
- 附錄:gdb工具的使用
- 附錄:lsof工具的使用
- 附錄:perf工具的使用
- 附錄:編譯PHP擴展的相關工具
- 備用:已移除的歷史特性
- swoole_server->handler
- task_worker_max
- swoole_server->addtimer
- swoole_server->deltimer
- onTimer
- swoole_timer_add
- swoole_timer_del
- swoole_get_mysqli_sock
- swoole_mysql_query
- onMasterConnect
- onMasterClose
- Nginx/Golang/Swoole/Node.js的性能對比
- Coroutine::call_user_func
- Coroutine::call_user_func_array
- Coroutine\Channel::select
- task_async
- 歷史:版本更新記錄(1.x)
- 1.10.3
- 1.10.2
- 1.10.1
- 1.10.0
- 1.9.23
- 1.9.22
- 1.9.19
- 1.9.18
- 1.9.17
- 1.9.16
- 1.9.15
- 1.9.14
- 1.9.12
- 1.9.11
- 1.9.9
- 1.9.7
- 1.9.6
- 1.9.5
- 1.9.4
- 1.9.3
- 1.9.2
- 1.9.1
- 1.9.0
- 1.8.13
- 1.8.12
- 1.8.11
- 1.8.10
- 1.8.9
- 1.8.8
- 1.8.7
- 1.8.6
- 1.8.5
- 1.8.4
- 1.8.3
- 1.8.2
- 1.8.1
- 1.8.0
- 1.7.22
- 1.7.21
- 1.7.20
- 1.7.19
- 1.7.18
- 1.7.17
- 1.7.16
- 1.7.15
- 1.7.14
- 1.7.13
- 1.7.12
- 1.7.11
- 1.7.10
- 1.7.9
- 1.7.8
- 1.7.7
- 1.7.6
- 1.7.5
- v1.5
- v1.6
- v1.7
- 歷史:版本更新記錄(2.x)
- 2.0.1-Alpha
- 2.0.5
- 2.0.9
- 1.9.21
- 2.0.10
- 2.0.11
- 2.0.12
- 2.0.13
- 2.1.1
- 2.1.2
- 2.2.0
- 3.0.0
- 歷史:版本更新記錄(4.x)
- 4.0.3
- 4.0.2
- 4.0.4
- 4.1.1
- 4.1.2
- 4.2.1
- 4.2.2
- 4.2.3
- 4.2.4
- 4.2.5
- 4.2.6
- 4.2.7
- 4.2.9
- 4.2.8
- 社區文檔版權申明
- 社區文檔編輯條例