摘要:的部分是基于以及協(xié)議的。例如父進(jìn)程向中寫入子進(jìn)程從中讀取子進(jìn)程向中寫入父進(jìn)程從中讀取。默認(rèn)使用對(duì)進(jìn)程進(jìn)行分配交給對(duì)應(yīng)的線程進(jìn)行監(jiān)聽線程收到某個(gè)進(jìn)程的數(shù)據(jù)后會(huì)進(jìn)行處理值得注意的是這個(gè)線程可能并不是發(fā)送請(qǐng)求的那個(gè)線程。
作者:施洪寶
一. 基礎(chǔ)知識(shí) 1.1 swooleswoole是面向生產(chǎn)環(huán)境的php異步網(wǎng)絡(luò)通信引擎, php開發(fā)人員可以利用swoole開發(fā)出高性能的server服務(wù)。swoole的server部分, 內(nèi)容很多, 也涉及很多的知識(shí)點(diǎn), 本文僅對(duì)其server進(jìn)行簡單的概述, 具體的實(shí)現(xiàn)細(xì)節(jié)在后續(xù)的文章中再進(jìn)行詳細(xì)介紹。
1.2 網(wǎng)絡(luò)編程網(wǎng)絡(luò)通信是指在一臺(tái)(或者多臺(tái))機(jī)器上啟動(dòng)一個(gè)(或者多個(gè))進(jìn)程, 監(jiān)聽一個(gè)(或者多個(gè))端口, 按照某種協(xié)議(可以是標(biāo)準(zhǔn)協(xié)議http, dns; 也可以是自行定義的協(xié)議)與客戶端交換信息。
目前的網(wǎng)絡(luò)編程多是在tcp, udp或者更上層的協(xié)議之上進(jìn)行編程。swoole的server部分是基于tcp以及udp協(xié)議的。
利用udp進(jìn)行編程較為簡單, 本文主要介紹tcp協(xié)議之上的網(wǎng)絡(luò)編程
TCP網(wǎng)絡(luò)編程主要涉及4種事件,
連接建立: 主要是指客戶端發(fā)起連接(connect)以及服務(wù)端接受連接(accept)
消息到達(dá): 服務(wù)端接受到客戶端發(fā)送的數(shù)據(jù), 該事件是TCP網(wǎng)絡(luò)編程最重要的事件, 服務(wù)端對(duì)于該類事件進(jìn)行處理時(shí), 可以采用阻塞式或者非阻塞式, 除此之外, 服務(wù)端還需要考慮分包, 應(yīng)用層緩沖區(qū)等問題
消息發(fā)送成功: 發(fā)送成功是指應(yīng)用層將數(shù)據(jù)成功發(fā)送到內(nèi)核的套接字發(fā)送緩沖區(qū)中, 并不是指客戶端成功接受數(shù)據(jù)。對(duì)于低流量的服務(wù)而言, 數(shù)據(jù)通常一次性即可發(fā)送完, 并不需要關(guān)心此類事件。如果一次性不能將全部數(shù)據(jù)發(fā)送到內(nèi)核緩沖區(qū), 則需要關(guān)心消息是否成功發(fā)送(阻塞式編程在系統(tǒng)調(diào)用(write, writev, send等)返回后即是發(fā)送成功, 非阻塞式編程則需要考慮實(shí)際寫入的數(shù)據(jù)是否與預(yù)期一致)
連接斷開: 需要考慮客戶端斷開連接(read返回0)以及服務(wù)端斷開連接(close, shutdown)
1.3 進(jìn)程間通信進(jìn)程之間的通信有無名管道(pipe), 有名管道(fifo), 信號(hào), 信號(hào)量, 套接字, 共享內(nèi)存等方式
swoole中采用unix域套接字用于多進(jìn)程之間的通信(指swoole內(nèi)部進(jìn)程之間)
1.4 socketpairsocketpair用于創(chuàng)建一個(gè)套接字對(duì), 類似于pipe, 不同的是pipe是單向通信, 雙向通信需要?jiǎng)?chuàng)建兩次, socketpair調(diào)用一次即可實(shí)現(xiàn)雙向通信, 除此之外, 由于使用的是套接字, 還可以定義數(shù)據(jù)交換的方式
socketpair系統(tǒng)調(diào)用
int socketpair(int domain, int type, int protocol, int sv[2]); //domain表示協(xié)議簇 //type表示類型 //protocol表示協(xié)議, SOCK_STREAM表示流協(xié)議(類似tcp), SOCK_DGRAM表示數(shù)據(jù)報(bào)協(xié)議(類似udp) //sv用于存儲(chǔ)建立的套接字對(duì), 也就是兩個(gè)套接字文件描述符 //成功返回0, 否則返回-1, 可以從errno獲取錯(cuò)誤信息
調(diào)用成功后sv[0], sv[1]分別存儲(chǔ)一個(gè)文件描述符
向sv[0]中寫入, 可以從sv[1]中讀取
向sv[1]中寫入, 可以從sv[0]中讀取
進(jìn)程調(diào)用socketpair后, fork子進(jìn)程, 子進(jìn)程會(huì)默認(rèn)繼承sv[0], sv[1]這兩個(gè)文件描述符, 進(jìn)而可以實(shí)現(xiàn)父子進(jìn)程間通信。例如, 父進(jìn)程向sv[0]中寫入, 子進(jìn)程從sv[1]中讀取; 子進(jìn)程向sv[1]中寫入, 父進(jìn)程從sv[0]中讀取。
1.5 守護(hù)進(jìn)程(daemon)守護(hù)進(jìn)程是一種特殊的后臺(tái)進(jìn)程, 它脫離于終端, 用于周期性的執(zhí)行某種任務(wù)
進(jìn)程組
每個(gè)進(jìn)程都屬于一個(gè)進(jìn)程組
每個(gè)進(jìn)程組都有一個(gè)進(jìn)程組號(hào), 也就是該組組長的進(jìn)程號(hào)(PID)
一個(gè)進(jìn)程只能為自己或者其子進(jìn)程設(shè)置進(jìn)程組號(hào)
會(huì)話
一個(gè)會(huì)話可以包含多個(gè)進(jìn)程組, 這些進(jìn)程組中最多只能有一個(gè)前臺(tái)進(jìn)程組(也可以沒有)
setsid可以創(chuàng)建一個(gè)新的會(huì)話, 該進(jìn)程不能是進(jìn)程組的組長。setsid調(diào)用完成后, 該進(jìn)程成為這個(gè)會(huì)話的首進(jìn)程(領(lǐng)頭進(jìn)程), 同時(shí)變成一個(gè)新的進(jìn)程組的組長, 如果該進(jìn)程之前有控制終端, 則該進(jìn)程與終端的聯(lián)系被斷開
用戶通過終端登錄或者網(wǎng)絡(luò)登錄, 會(huì)創(chuàng)建一個(gè)新的會(huì)話
一個(gè)會(huì)話最多只能有一個(gè)控制終端
創(chuàng)建守護(hù)進(jìn)程的方式
fork子進(jìn)程后, 父進(jìn)程退出, 子進(jìn)程執(zhí)行setsid即可成為守護(hù)進(jìn)程。這種方式下, 子進(jìn)程是會(huì)話的領(lǐng)頭進(jìn)程, 可以重新打開終端, 此時(shí)可以再次fork, fork產(chǎn)生的子進(jìn)程無法再打開終端。第二次fork并不是必須的, 只是為了防止子進(jìn)程再次打開終端
linux提供了daemon函數(shù)用于創(chuàng)建守護(hù)進(jìn)程
1.6 swoole tcp server示例set(array( "reactor_num" => 2, //reactor thread num "worker_num" => 3, //worker process num )); //設(shè)置事件回調(diào) $serv->on("connect", function ($serv, $fd){ echo "Client:Connect. "; }); $serv->on("receive", function ($serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: ".$data); $serv->close($fd); }); $serv->on("close", function ($serv, $fd) { echo "Client: Close. "; }); //啟動(dòng)server $serv->start();
上述代碼在cli模式下執(zhí)行時(shí), 經(jīng)過詞法分析, 語法分析生成opcode, 進(jìn)而交由zend虛擬機(jī)執(zhí)行
zend虛擬機(jī)在執(zhí)行到$serv->start()時(shí), 啟動(dòng)swoole server
上述代碼中設(shè)置的事件回調(diào)是在worker進(jìn)程中執(zhí)行, 后文會(huì)詳細(xì)介紹swoole server模型
二. swoole server 2.1 base模式說明
base模式采用多進(jìn)程模型, 這種模型與nginx一致, 每個(gè)進(jìn)程只有一個(gè)線程, 主進(jìn)程負(fù)責(zé)管理工作進(jìn)程, 工作進(jìn)程負(fù)責(zé)監(jiān)聽端口, 接受連接, 處理請(qǐng)求以及關(guān)閉連接
多個(gè)進(jìn)程同時(shí)監(jiān)聽端口, 會(huì)有驚群問題, 目前swoole并沒有解決
linux 內(nèi)核3.9及其后續(xù)版本提供了新的套接字參數(shù)SO_REUSEPORT, 該參數(shù)允許多個(gè)進(jìn)程綁定到同一個(gè)端口, 內(nèi)核在接受到新的連接請(qǐng)求時(shí), 會(huì)喚醒其中一個(gè)進(jìn)行處理, 內(nèi)核層面也會(huì)做負(fù)載均衡, 可以解決上述的驚群問題
base模式下, reactor_number參數(shù)并沒有作用, 因?yàn)槊總€(gè)進(jìn)程只有一個(gè)線程
如果worker進(jìn)程數(shù)設(shè)置為1, 則不會(huì)fork出worker進(jìn)程, 主進(jìn)程直接處理請(qǐng)求
啟動(dòng)過程
php代碼執(zhí)行到$serv->start()時(shí), 主進(jìn)程進(jìn)入int swServer_start(swServer *serv)函數(shù), 該函數(shù)負(fù)責(zé)啟動(dòng)server
在函數(shù)swServer_start中會(huì)調(diào)用swReactorProcess_start, 這個(gè)函數(shù)會(huì)fork出多個(gè)worker進(jìn)程
主進(jìn)程和worker進(jìn)程各自進(jìn)入自己的事件循環(huán), 處理各類事件
2.2 process模式說明
這種模式為多進(jìn)程多線程, 有主進(jìn)程, manager進(jìn)程, worker進(jìn)程, task_worker進(jìn)程
主進(jìn)程下有多個(gè)線程, 主線程負(fù)責(zé)接受連接, 之后交給react線程處理請(qǐng)求。 react線程負(fù)責(zé)接收數(shù)據(jù)包, 并將數(shù)據(jù)轉(zhuǎn)發(fā)給worker進(jìn)程進(jìn)行處理, 之后處理worker進(jìn)程返回的數(shù)據(jù)
manager進(jìn)程, 該進(jìn)程為單線程, 主要負(fù)責(zé)管理worker進(jìn)程, 類似于nginx中的主進(jìn)程, 當(dāng)worker進(jìn)程異常退出時(shí), manager進(jìn)程負(fù)責(zé)重新fork出一個(gè)worker進(jìn)程
worker進(jìn)程, 該進(jìn)程為單線程, 負(fù)責(zé)具體處理請(qǐng)求
task_worker進(jìn)程, 用于處理比較耗時(shí)的任務(wù), 默認(rèn)不開啟
worker進(jìn)程與主進(jìn)程中的react線程使用域套接字進(jìn)行通信, worker進(jìn)程之間不進(jìn)行通信
啟動(dòng)過程
swoole server啟動(dòng)入口: swServer_start函數(shù),
//php 代碼中$serv->start(); 會(huì)調(diào)用函數(shù), 進(jìn)行server start int swServer_start(swServer *serv); // 該函數(shù)首先進(jìn)行必要的參數(shù)檢查 static int swServer_start_check(swServer *serv); // 其中有, if (serv->worker_num < serv->reactor_num) { serv->reactor_num = serv->worker_num; }//也就是說reactor_num <= worker_num //之后執(zhí)行factory start, 也就是swFactoryProcess_start函數(shù), 該函數(shù)會(huì)fork出manager進(jìn)程, manager進(jìn)程進(jìn)而fork出worker進(jìn)程以及task_worker進(jìn)程 if (factory->start(factory) < 0) { return SW_ERR; } //然后主進(jìn)程的主線程生成reactor線程 if (serv->factory_mode == SW_MODE_BASE) { ret = swReactorProcess_start(serv); } else { ret = swReactorThread_start(serv); }
如果設(shè)置了daemon模式, 在必要的參數(shù)檢查完后, 先將自己變?yōu)槭刈o(hù)進(jìn)程再fork manager進(jìn)程, 進(jìn)而創(chuàng)建reactor線程
主進(jìn)程先fork出manager進(jìn)程, manager進(jìn)程負(fù)責(zé)fork出worker進(jìn)程以及task_worker進(jìn)程。worker進(jìn)程之后進(jìn)入int swWorker_loop(swServer *serv, int worker_id), 也就是進(jìn)入自己的事件循環(huán), task_worker也是一樣, 進(jìn)入自己的事件循環(huán)。
static int swFactoryProcess_start(swFactory *factory); //swFactoryProcess_start會(huì)調(diào)用swManager_start生成manager進(jìn)程 int swManager_start(swServer *serv); // manager進(jìn)程會(huì)fork出worker進(jìn)程以及task_worker進(jìn)程
主進(jìn)程pthread_create出react線程, 主線程和react線程各自進(jìn)入自己的事件循環(huán), reactor線程執(zhí)行static int swReactorThread_loop(swThreadParam *param), 等待處理事件
//主線程執(zhí)行swReactorThread_start, 創(chuàng)建出reactor線程 int swReactorThread_start(swServer *serv);
結(jié)構(gòu)圖
swoole process模式結(jié)構(gòu)如下圖所示,
上圖并沒有考慮task_worker進(jìn)程, 在默認(rèn)情況下, task_worker進(jìn)程數(shù)為0
三. 請(qǐng)求處理流程(process模式) 3.1 reactor線程與worker進(jìn)程之間的通信swoole master進(jìn)程與worker進(jìn)程之間的通信如下圖所示,
swoole使用SOCK_DGRAM, 而不是SOCK_STREAM, 這里是因?yàn)槊總€(gè)reactor線程負(fù)責(zé)處理多個(gè)請(qǐng)求, reactor接收到請(qǐng)求后會(huì)將信息轉(zhuǎn)發(fā)給worker進(jìn)程, 由worker進(jìn)程負(fù)責(zé)處理,如果使用SOCK_STREAM, worker進(jìn)程無法對(duì)tcp進(jìn)行分包, 進(jìn)而處理請(qǐng)求
swFactoryProcess_start函數(shù)中會(huì)根據(jù)worker進(jìn)程數(shù)創(chuàng)建對(duì)應(yīng)個(gè)數(shù)的套接字對(duì), 用于reactor線程與worker進(jìn)程通信(swPipeUnsock_create函數(shù))
假設(shè)reactor線程有2個(gè), worker進(jìn)程有3個(gè), 則reactor與worker之間的通信如下圖所示,
每個(gè)reactor線程負(fù)責(zé)監(jiān)聽幾個(gè)worker進(jìn)程, 每個(gè)worker進(jìn)程只有一個(gè)reactor線程監(jiān)聽(reactor_num<=worker_num)。swoole默認(rèn)使用worker_process_id % reactor_num對(duì)worker進(jìn)程進(jìn)行分配, 交給對(duì)應(yīng)的reactor線程進(jìn)行監(jiān)聽
reactor線程收到某個(gè)worker進(jìn)程的數(shù)據(jù)后會(huì)進(jìn)行處理, 值得注意的是, 這個(gè)reactor線程可能并不是發(fā)送請(qǐng)求的那個(gè)reactor線程。
reactor線程與worker進(jìn)程通信的數(shù)據(jù)包
//包頭 typedef struct _swDataHead { int fd; uint32_t len; int16_t from_id; uint8_t type; uint8_t flags; uint16_t from_fd; #ifdef SW_BUFFER_RECV_TIME double time; #endif } swDataHead; //reactor線程向worker進(jìn)程發(fā)送的數(shù)據(jù), 也就是worker進(jìn)程收到的數(shù)據(jù)包 typedef struct { swDataHead info; char data[SW_IPC_BUFFER_SIZE]; } swEventData; //worker進(jìn)程向reactor線程發(fā)送的數(shù)據(jù), 也就是reactor線程收到的數(shù)據(jù)包 typedef struct { swDataHead info; char data[0]; } swPipeBuffer;3.2 請(qǐng)求處理
master進(jìn)程中的主線程負(fù)責(zé)監(jiān)聽端口(listen), 接受連接(accept, 產(chǎn)生一個(gè)fd), 接受連接后將請(qǐng)求分配給reactor線程, 默認(rèn)通過fd % reactor_num進(jìn)行分配, 之后通過epoll_ctl將fd加入到對(duì)應(yīng)reactor線程中(如果對(duì)應(yīng)的reactor線程正在執(zhí)行epoll_wait, 主線程會(huì)阻塞), 剛加入時(shí)監(jiān)聽寫事件, 如果直接監(jiān)聽讀事件, 可能會(huì)立刻被觸發(fā), 而監(jiān)聽寫事件可以允許reactor線程進(jìn)行一些初始化操作
//主線程執(zhí)行epoll_ctl將fd(新接受的連接)加入到reactor線程的監(jiān)聽隊(duì)列中 epoll_ctl(epfd, fd, ...); //對(duì)應(yīng)的reactor線程如果正在執(zhí)行 epoll_wait(epfd, ...);
這種情況主線程會(huì)被阻塞(兩個(gè)線程同時(shí)操作epfd)
如果reactor線程沒有正在執(zhí)行epoll_wait, 主線程則不會(huì)被阻塞, 執(zhí)行成功后直接返回
reactor線程中fd的寫事件被觸發(fā), reactor線程負(fù)責(zé)處理, 發(fā)現(xiàn)是首次加入, 沒有數(shù)據(jù)可寫, 則開啟讀事件監(jiān)聽
reactor線程讀取到用戶的請(qǐng)求數(shù)據(jù), 一個(gè)請(qǐng)求的數(shù)據(jù)接收完后, 將數(shù)據(jù)轉(zhuǎn)發(fā)給worker進(jìn)程, 默認(rèn)是通過fd % worker_num進(jìn)行分配
reactor發(fā)送給worker進(jìn)程的數(shù)據(jù)包, 會(huì)包含一個(gè)頭部, 頭部中記錄了reactor的信息
如果發(fā)送的數(shù)據(jù)過大, 則需要將數(shù)據(jù)進(jìn)行分片, 限于篇幅, reactor的分片, 后續(xù)再進(jìn)行詳細(xì)講述
可能存在多個(gè)reactor線程同時(shí)向同一個(gè)worker進(jìn)程發(fā)送數(shù)據(jù)的情況, 故而swoole采用SOCK_DGRAM模式與worker進(jìn)程進(jìn)行通信, 通過每個(gè)數(shù)據(jù)包的包頭, worker進(jìn)程可以區(qū)分出是由哪個(gè)reactor線程發(fā)送的數(shù)據(jù)
worker進(jìn)程收到reactor發(fā)送的數(shù)據(jù)包后, 進(jìn)行處理, 處理完成后, 將數(shù)據(jù)發(fā)送給主進(jìn)程
worker進(jìn)程發(fā)送給主進(jìn)程的數(shù)據(jù)包, 也會(huì)包含一個(gè)頭部, 當(dāng)reactor線程收到數(shù)據(jù)包后, 能夠知道對(duì)應(yīng)的reactor線程, 請(qǐng)求的fd等信息
主進(jìn)程收到worker進(jìn)程發(fā)送的數(shù)據(jù)包, 這個(gè)會(huì)觸發(fā)某個(gè)reactor線程進(jìn)行處理
這個(gè)reactor線程并不一定是之前發(fā)送請(qǐng)求給worker進(jìn)程的那個(gè)reactor線程
主進(jìn)程的每個(gè)reactor線程都負(fù)責(zé)監(jiān)聽worker進(jìn)程發(fā)送的數(shù)據(jù)包, 每個(gè)worker發(fā)送的數(shù)據(jù)包只會(huì)由一個(gè)reactor線程進(jìn)行監(jiān)聽, 故而只會(huì)觸發(fā)一個(gè)reactor線程
reactor線程處理worker進(jìn)程發(fā)送的數(shù)據(jù)包, 如果是直接發(fā)送數(shù)據(jù)給客戶端, 則可以直接發(fā)送, 如果需要改變這個(gè)這個(gè)連接的監(jiān)聽狀態(tài)(例如close), 則需要先找到監(jiān)聽這個(gè)連接的reactor, 進(jìn)而改變這個(gè)連接的監(jiān)聽狀態(tài)
reactor處理線程與reactor監(jiān)聽線程可能并不是同一個(gè)線程
reactor監(jiān)聽線程負(fù)責(zé)監(jiān)聽客戶端發(fā)送的數(shù)據(jù), 進(jìn)而轉(zhuǎn)發(fā)給worker進(jìn)程
reactor處理線程負(fù)責(zé)監(jiān)聽worker進(jìn)程發(fā)送給主進(jìn)程的數(shù)據(jù), 進(jìn)而將數(shù)據(jù)發(fā)送給客戶端
四. gdb調(diào)試 4.1 process模式啟動(dòng)//fork manager進(jìn)程 #0 0x00007ffff67dae64 in fork () from /lib64/libc.so.6 #1 0x00007ffff553888a in swoole_fork () at /root/code/swoole-src/src/core/base.c:186 #2 0x00007ffff556afb8 in swManager_start (serv=serv@entry=0x1353f60) at /root/code/swoole-src/src/server/manager.cc:164 #3 0x00007ffff5571dde in swFactoryProcess_start (factory=0x1353ff8) at /root/code/swoole-src/src/server/process.c:198 #4 0x00007ffff556ef8b in swServer_start (serv=0x1353f60) at /root/code/swoole-src/src/server/master.cc:651 #5 0x00007ffff55dc808 in zim_swoole_server_start (execute_data=4.2 base模式啟動(dòng), return_value=0x7fffffffac50) at /root/code/swoole-src/swoole_server.cc:2946 #6 0x00000000007bb068 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /root/php-7.3.3/Zend/zend_vm_execute.h:980 #7 execute_ex (ex=0x7ffff7f850a8) at /root/php-7.3.3/Zend/zend_vm_execute.h:55485 #8 0x00000000007bbf58 in zend_execute (op_array=op_array@entry=0x7ffff5e7b340, return_value=return_value@entry=0x7ffff5e1d030) at /root/php-7.3.3/Zend/zend_vm_execute.h:60881 #9 0x0000000000737554 in zend_execute_scripts (type=type@entry=8, retval=0x7ffff5e1d030, retval@entry=0x0, file_count=file_count@entry=3) at /root/php-7.3.3/Zend/zend.c:1568 #10 0x00000000006db4d0 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd050) at /root/php-7.3.3/main/main.c:2630 #11 0x00000000007be2f5 in do_cli (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:997 #12 0x000000000043fc1f in main (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:1389 // pthread_create reactor線程 #0 0x00007ffff552e960 in pthread_create@plt () from /usr/local/lib/php/extensions/no-debug-non-zts-20180731/swoole.so #1 0x00007ffff5576959 in swReactorThread_start (serv=0x1353f60) at /root/code/swoole-src/src/server/reactor_thread.c:883 #2 0x00007ffff556f006 in swServer_start (serv=0x1353f60) at /root/code/swoole-src/src/server/master.cc:670 #3 0x00007ffff55dc808 in zim_swoole_server_start (execute_data= , return_value=0x7fffffffac50) at /root/code/swoole-src/swoole_server.cc:2946 #4 0x00000000007bb068 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /root/php-7.3.3/Zend/zend_vm_execute.h:980 #5 execute_ex (ex=0x7fffffffab10) at /root/php-7.3.3/Zend/zend_vm_execute.h:55485 #6 0x00000000007bbf58 in zend_execute (op_array=op_array@entry=0x7ffff5e7b340, return_value=return_value@entry=0x7ffff5e1d030) at /root/php-7.3.3/Zend/zend_vm_execute.h:60881 #7 0x0000000000737554 in zend_execute_scripts (type=type@entry=8, retval=0x7ffff5e1d030, retval@entry=0x0, file_count=file_count@entry=3) at /root/php-7.3.3/Zend/zend.c:1568 #8 0x00000000006db4d0 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd050) at /root/php-7.3.3/main/main.c:2630 #9 0x00000000007be2f5 in do_cli (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:997 #10 0x000000000043fc1f in main (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:1389
//base 模式下的啟動(dòng) #0 0x00007ffff67dae64 in fork () from /lib64/libc.so.6 #1 0x00007ffff553888a in swoole_fork () at /root/code/swoole-src/src/core/base.c:186 #2 0x00007ffff5558557 in swProcessPool_spawn (pool=pool@entry=0x7ffff2d2a308, worker=0x7ffff2d2a778) at /root/code/swoole-src/src/network/process_pool.c:392 #3 0x00007ffff5558710 in swProcessPool_start (pool=0x7ffff2d2a308) at /root/code/swoole-src/src/network/process_pool.c:227 #4 0x00007ffff55741cf in swReactorProcess_start (serv=0x1353f60) at /root/code/swoole-src/src/server/reactor_process.cc:176 #5 0x00007ffff556f21d in swServer_start (serv=0x1353f60) at /root/code/swoole-src/src/server/master.cc:666 #6 0x00007ffff55dc808 in zim_swoole_server_start (execute_data=五. 參考, return_value=0x7fffffffac50) at /root/code/swoole-src/swoole_server.cc:2946 #7 0x00000000007bb068 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER () at /root/php-7.3.3/Zend/zend_vm_execute.h:980 #8 execute_ex (ex=0x7ffff2d2a308) at /root/php-7.3.3/Zend/zend_vm_execute.h:55485 #9 0x00000000007bbf58 in zend_execute (op_array=op_array@entry=0x7ffff5e7b340, return_value=return_value@entry=0x7ffff5e1d030) at /root/php-7.3.3/Zend/zend_vm_execute.h:60881 #10 0x0000000000737554 in zend_execute_scripts (type=type@entry=8, retval=0x7ffff5e1d030, retval@entry=0x0, file_count=file_count@entry=3) at /root/php-7.3.3/Zend/zend.c:1568 #11 0x00000000006db4d0 in php_execute_script (primary_file=primary_file@entry=0x7fffffffd050) at /root/php-7.3.3/main/main.c:2630 #12 0x00000000007be2f5 in do_cli (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:997 #13 0x000000000043fc1f in main (argc=2, argv=0x1165cd0) at /root/php-7.3.3/sapi/cli/php_cli.c:1389
UNIX網(wǎng)絡(luò)編程
UNIX環(huán)境高級(jí)編程
https://wiki.swoole.com/
https://www.cnblogs.com/welhz...
https://www.cnblogs.com/JohnA...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31602.html
摘要:若參數(shù)和字符串相等則返回。大于則返回大于的值,小于則返回小于的值。下面的中是增加一個(gè)這里我們顯然設(shè)置了主機(jī)和端口暫且跳過在這里我們看到調(diào)用了中的方法并且將初始化好的等傳了進(jìn)去,我們追進(jìn)去線程數(shù) 創(chuàng)建一個(gè)server 今天我們來研究一下swoole中server相關(guān)的源碼(版本是4.3.1),首先我們先從一段簡單代碼開始 $http = new SwooleHttpServer(127....
摘要:和服務(wù)關(guān)系最密切的進(jìn)程是中的進(jìn)程組,絕大部分業(yè)務(wù)處理都在該進(jìn)程中進(jìn)行。隨后觸發(fā)一個(gè)事件各組件通過該事件進(jìn)行配置文件加載路由注冊。事件每個(gè)請(qǐng)求到來時(shí)僅僅會(huì)觸發(fā)事件。服務(wù)器生命周期和服務(wù)基本一致,詳情參考源碼剖析功能實(shí)現(xiàn) 作者:bromine鏈接:https://www.jianshu.com/p/4c0...來源:簡書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。S...
摘要:此時(shí)的協(xié)程實(shí)現(xiàn)無法完美的支持語法,其根本原因在于沒有保存棧信息。這是因?yàn)檎{(diào)用函數(shù)時(shí),底層指令已經(jīng)將入棧了。協(xié)程創(chuàng)建時(shí),底層通過函數(shù)實(shí)現(xiàn)了棧的創(chuàng)建創(chuàng)建并初始化棧為結(jié)構(gòu)分配空間創(chuàng)建新的執(zhí)行數(shù)據(jù)結(jié)構(gòu)從代碼中可以看到結(jié)構(gòu)是直接存儲(chǔ)在棧的底部。 作者:李樂??本文基于Swoole-4.3.2和PHP-7.1.0版本 Swoole協(xié)程簡介 ??Swoole4為PHP語言提供了強(qiáng)大的CSP協(xié)程編程模...
摘要:在中的應(yīng)用官網(wǎng)源碼解讀號(hào)外號(hào)外歡迎大家我們開發(fā)組定了一個(gè)就線下聚一次的小目標(biāo)上一篇源碼解讀反響還不錯(cuò)不少同學(xué)推薦再加一篇講解一下中使用到的功能幫助大家開啟的實(shí)戰(zhàn)之旅服務(wù)器開發(fā)涉及到的相關(guān)技術(shù)領(lǐng)域的知識(shí)非常多不日積月累打好基礎(chǔ)是很難真正 date: 2017-12-14 21:34:51title: swoole 在 swoft 中的應(yīng)用 swoft 官網(wǎng): https://www.sw...
摘要:服務(wù)重點(diǎn)基本概述協(xié)議是基于的一種新的網(wǎng)絡(luò)協(xié)議。被調(diào)用者通過狀態(tài)通知機(jī)制等來通知調(diào)用者,或通過回調(diào)函數(shù)來處理結(jié)果阻塞和非阻塞關(guān)注的是調(diào)用者等待被調(diào)用者返回調(diào)用結(jié)果時(shí)的狀態(tài)。 一、PHP7源碼安裝和Swoole源碼編譯安裝 1.1 PHP7源碼安裝 1.1.1 獲取源碼與安裝 ????獲取PHP7源碼:www.php.net tar -xzvf ... # 解壓命令 ./configur...
閱讀 2940·2021-11-04 16:06
閱讀 775·2021-09-30 09:56
閱讀 1841·2021-09-22 10:02
閱讀 2622·2019-08-29 13:43
閱讀 2218·2019-08-29 13:42
閱讀 2300·2019-08-29 12:21
閱讀 1056·2019-08-29 11:29
閱讀 1387·2019-08-26 13:51