PHP pcntl

pcntl是一個可以利用操作系統(tǒng)的fork系統(tǒng)調(diào)用在PHP中實現(xiàn)多線程的進程控制擴展,當使用fork系統(tǒng)調(diào)用后執(zhí)行的代碼將會是并行的。pcntl僅適用于Linux平臺的CLI模式下使用。

PHP官方?jīng)]有提供多線程的擴展,在pecl中有一個pthread擴展提供了多線程的特性,此版本僅在線程安全版本中可用。

創(chuàng)建子進程pcntl_fork

int pcntl_fork(void)

pcntl_fork函數(shù)執(zhí)行時會在當前進程下創(chuàng)建一個子進程,子進程與父進程在PID和PPID上會不同。

子進程會復制父進程中所有的數(shù)據(jù)、代碼、狀態(tài)等信息。當使用pcntl_fork成功創(chuàng)建子進程后,子進程會復制父進程的代碼和數(shù)據(jù)。此時父進程和子進程擁有相同的代碼和數(shù)據(jù)。子進程也會復制父進程的狀態(tài)。

當使用pcntl_fork創(chuàng)建子進程,如果成功則會在父進程中將會返回0,在子進程中會返回自身的進程編號PID。如果創(chuàng)建失敗則返回-1。

使用pcntl_fork創(chuàng)建的進程只是一個分支節(jié)點,相當于一個標記,父進程完成后子進程會從標記處繼續(xù)執(zhí)行,也就是說在pcntl_fork之后的代碼分別會被父進程和子進程執(zhí)行兩遍,而兩個進程在執(zhí)行過程中得到的返回值卻是不同的,因此才可以分離父子進程執(zhí)行不同的代碼。

在Linux環(huán)境下可使用ps命令查看進程

<?php
$pid = pcntl_fork();
if($pid > 0){
    //父進程
    exit(0);
}elseif($pid == 0){
    //子進程
    exit(0);
}

多進程和多線程的作用相同,區(qū)別主要在于

  • 多個線程是在同一個進程內(nèi)的,線程之間可以共享內(nèi)存變量而實現(xiàn)線程間的通信。
  • 線程比進程更加輕量級,進程要比線程更加消耗系統(tǒng)資源。

多線程存在的問題主要有

  • 線程讀寫變量存在著同步問題需要加鎖
  • 鎖粒度過大會存在性能問題,會導致只有一個線程在運行,其它線程都在等待鎖,也就無法實現(xiàn)并行。
  • 同時使用多個鎖時邏輯復雜,一旦某個鎖沒有被正確釋放可能會發(fā)生線程死鎖。
  • 某個線程發(fā)生致命錯誤會導致整個進程崩潰

相對而言多進程更為穩(wěn)定,可利用進程間通信IPC技術實現(xiàn)數(shù)據(jù)共享。多進程通信的方式主要包括

  • 共享內(nèi)存
    共享內(nèi)存和線程間讀寫變量時一樣的,都需要加鎖,同時也存在同步、死鎖等問題。
  • 消息隊列
    消息隊列采用多個子進程搶占隊列的模式,性能較好。
  • 管道、UnixSock、TCP、UDP
    可以使用read/write來傳遞數(shù)據(jù),TCP/UDP使用socket來通信,子進程可以分布運行。

利用fork系統(tǒng)調(diào)用可以實現(xiàn)并發(fā)的TCP服務器,主進程accept客戶端連接。當有新的連接到來時直接fork一個子進程,子進程中循環(huán)recv/send處理數(shù)據(jù)。這種模式在請求量不多的情況下很實用,例如FTP服務器。

在過去多數(shù)Linux程序都時采用這種模式,簡單高效,代碼量少。當有幾百個并發(fā)的情況下表現(xiàn)不錯,但在大并發(fā)的情況下消耗就會過大。

例如:每個子進程都能創(chuàng)建一個與之對應的文件,父進程也創(chuàng)建一個屬于自己的文件。

<?php
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
if($socket < 0){
    $errmsg = socket_strerror($socket);
    echo "failed to create socket: {$errmsg}".PHP_EOL;
    exit;
}

$host = "0.0.0.0";
$port = 9601;
$ret = socket_bind($socket, $host, $port);
if($ret < 0){
    echo "failed to bind socket: {$ret}".PHP_EOL;
    exit;
}

$ret = socket_listen($socket, 0);
if($ret < 0){
    $errmsg = socket_strerror($ret);
    echo "failed to listen: {$errmsg}".PHP_EOL;
    exit;
}

while(pcntl_fork() == 0){
    $connection = @socket_accept($socket);
    if(pcntl_fork() == 0){
        $recv =  socket_read($connection ,8192);
        $data = "serverr: {$recv}";

        socket_write($connection ,$data);
        socket_close($connection);
        exit(0);
    }else{
        socket_close($connection);
    }
}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 要搞清楚fork的執(zhí)行過程,就必須先弄清楚操作系統(tǒng)中”進程(process)”的概念。 一個進程,主要包含三個元素...
    金星show閱讀 712評論 0 3
  • 1.內(nèi)存的頁面置換算法 (1)最佳置換算法(OPT)(理想置換算法):從主存中移出永遠不再需要的頁面;如無這樣的...
    杰倫哎呦哎呦閱讀 3,588評論 1 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,631評論 1 32
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,706評論 1 4
  • 必備的理論基礎 1.操作系統(tǒng)作用: 隱藏丑陋復雜的硬件接口,提供良好的抽象接口。 管理調(diào)度進程,并將多個進程對硬件...
    drfung閱讀 3,755評論 0 5

友情鏈接更多精彩內(nèi)容