pcntl_fork()函數是php-pcntl模塊中用於創建進程的函數。(不支持windows)
至於php_pcntl擴展如何安裝開啟這里就不介紹了,只分析pcntl_fork()這個函數本身。
1.$one = 123; 2.$one++; 3.$two = time(); 4.$pid = []; 5.$pid = pcntl_fork(); 6.$three = time();
當:pcntl_fork()函數執行的時候,會創建一個子進程。子進程會復制當前進程,也就是父進程的所有:數據,代碼,還有狀態。
1.當pcntl_fork()創建子進程成功后,在父進程內,返回0,在子進程內返回自身的進程號,失敗則返回-1
2.子進程會復制父進程的代碼,數據。那么就說明:子,父進程擁有的代碼和數據會一模一樣。
3.重點:子進程會復制父進程的狀態,那么就有上面的示例代碼:在第五行執行了pcntl_fork,那么創建出的子進程,代碼也是從第五行開始執行的。又子進程復制了數據,代碼。所以,在子進程內同理存在:$one,$two等變量
for ($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); } sleep(30);
那么:上面的for循環,實際會產生多少個子進程?答案是7個,在linux下,用ps命令將可以看到8個進程(1個父進程,7個子進程)
原因:父進程在$i=0時,創建出一個子進程0,此時的子進程,還會繼續執行循環。創建出屬於自己的子進程。同理:$i=1時也會這樣……
PHP創建多進程的Demo示例:
<?php /** * PHP多進程和多線程的處理 */ //創建socket監聽 $socketserv = stream_socket_server('tcp://0.0.0.0:8000', $errno, $errstr); //創建5個子進程 for ($i = 0; $i < 5; $i++) { //使用pcntl_fork()創建進程,會返回pid,如果pid==0,則表示主進程 if (pcntl_fork() == 0) { //循環監聽 while (true) { $conn = stream_socket_accept($socketserv); //如果監聽失敗,則重新去監聽 if(!$conn){ continue; } //讀取流信息,讀取的大小 是9000 $request = fread($conn, 9000); //寫入響應 $response = 'hello'; fwrite($conn, $response); //關閉流 fclose($conn); } //創建完所有的子進程,然后退出 exit(0); } }
運行
php stream_socket.php,使用
ps -ef 查看進程,會看到多出了如下的5個進程:

擴展:PHP的異步非阻塞模型 Reactor:
Reactor有4個核心的操作:
- add 添加socket監聽到reactor
- set 修改事件監聽,可以設置監聽的類型,如可讀、可寫
- del 從reactor中移除,不再監聽事件
- callback 就是事件發生后對應的處理邏輯,一般在add/set時制定。
(C語言用函數指針實現,JS可以用匿名函數,PHP可以用匿名函數、對象方法數組、字符串函數名)
Reactor只是一個事件發生器,實際對socket句柄的操作,如
connect/accept、send/recv、close是在callback中完成的。
具體編碼可參考下面的代碼(需要先安裝Reactor擴展):

Reactor模型還可以與多進程、多線程結合起來用,既實現異步非阻塞IO,又利用到多核。
目前流行的異步服務器程序都是這樣的方式:如
- Nginx:多進程Reactor
- Nginx+Lua:多進程Reactor+協程
- Golang:單線程Reactor+多線程協程
- Swoole:多線程Reactor+多進程Worker