PHP多進程初探 --- 開篇

[原文地址:https://blog.ti-node.com/blog...]php

實際上PHP是有多線程的,只是不少人不經常使用。使用PHP的多線程首先須要下載安裝一個線程安全版本(ZTS版本)的PHP,而後再安裝pecl的pthread擴展node

實際上PHP是有多進程的,有一些人再用,整體來講php的多進程還算湊合,只須要在安裝PHP的時候開啓pcntl模塊(是否是跟UNIX中的fcntl有點兒.... ....)便可。在*NIX下,在終端命令行下使用php -m就能夠看到是否開啓了pcntl模塊。nginx

因此咱們只說php的多進程,至於php多線程就暫時放到一邊兒。apache

注意:不要在apache或者fpm環境下使用php多進程,這將會產生不可預估的後果。編程

進程是程序執行的實例,舉個例子有個程序叫作 「 病毒.exe 」,這個程序平時是以文件形式存儲在硬盤上,當你雙擊運行後,就會造成一個該程序的進程。系統會給每個進程分配一個惟一的非負整數用來標記進程,這個數字稱做進程ID。當該進程被殺死或終止後,其進程ID就會被系統回收,而後分配給新的其他的進程。安全

說了這麼多,這鬼東西有什麼用嗎?我平時用CI、YII寫個CURD跟這個也沒啥關聯啊。實際上,若是你瞭解APACHE PHP MOD或者FPM就知道這些東西就是多進程實現的。以FPM爲例,通常都是nginx做爲http服務器擋在最前面,靜態文件請求則nginx自行處理,遇到php動態請求則轉發給php-fpm進程來處理。若是你的php-fpm配置只開了5個進程,若是處理任意一個用戶的請求都須要1秒鐘,那麼5個fpm進程1秒中就最多隻能處5個用戶的請求。因此結論就是:若是要單位時間內幹活更快更多,就須要更多的進程,總之一句話就是多進程能夠加快任務處理速度。服務器

在php中咱們使用pcntl_fork()來建立多進程(在*NIX系統的C語言編程中,已有進程經過調用fork函數來產生新的進程)。fork出來新進程則成爲子進程,原進程則成爲父進程,子進程擁有父進程的副本。這裏要注意:多線程

  • 子進程與父進程共享程序正文段
  • 子進程擁有父進程的數據空間和堆、棧的副本,注意是副本,不是共享
  • 父進程和子進程將繼續執行fork以後的程序代碼
  • fork以後,是父進程先執行仍是子進程先執行沒法確認,取決於系統調度(取決於信仰)

這裏說子進程擁有父進程數據空間以及堆、棧的副本,實際上,在大多數的實現中也並非真正的徹底副本。更可能是採用了COW(Copy On Write)即寫時複製的技術來節約存儲空間。簡單來講,若是父進程和子進程都不修改這些 數據、堆、棧 的話,那麼父進程和子進程則是暫時共享同一份 數據、堆、棧。只有當父進程或者子進程試圖對 數據、堆、棧 進行修改的時候,纔會產生複製操做,這就叫作寫時複製。函數

在調用完pcntl_fork()後,該函數會返回兩個值。在父進程中返回子進程的進程ID,在子進程內部自己返回數字0。因爲多進程在apache或者fpm環境下沒法正常運行,因此你們必定要在php cli環境下執行下面php代碼。php-fpm

第一段代碼,咱們來講明在程序從pcntl_fork()後父進程和子進程將各自繼續往下執行代碼:

<?php
        $pid = pcntl_fork();
        if( $pid > 0 ){
          echo "我是父親".PHP_EOL;
        } else if( 0 == $pid ) {
          echo "我是兒子".PHP_EOL;
        } else {
          echo "fork失敗".PHP_EOL;
        }

將文件保存爲test.php,而後在使用cli執行,結果以下圖所示:

第二段代碼,用來講明子進程擁有父進程的數據副本,而並非共享:

<?php
        // 初始化一個 number變量 數值爲1
        $number = 1;
        $pid = pcntl_fork();
        if( $pid > 0 ){
          $number += 1;
          echo "我是父親,number+1 : { $number }".PHP_EOL;
        } else if( 0 == $pid ) {
          $number += 2;
          echo "我是父親,number+2 : { $number }".PHP_EOL;
        } else {
          echo "fork失敗".PHP_EOL;
        }

第三段代碼,比較容易讓人思惟混亂,pcntl_fork()配合for循環來作些東西,問題來了:會顯示幾回 「 兒子 」?

<?php
        for( $i = 1; $i <= 3 ; $i++ ){
            $pid = pcntl_fork();
            if( $pid > 0 ){
               // do nothing ...
            } else if( 0 == $pid ){
                echo "兒子".PHP_EOL;
            }
        }

上面代碼執行結果以下:

仔細數數,居然是顯示了7次 「 兒子 」。好奇怪,難道不是3次嗎?... ...
下面我修改一下代碼,結合下面的代碼,再思考一下爲何會產生7次而不是3次。

<?php
        for( $i = 1; $i <= 3 ; $i++ ){
            $pid = pcntl_fork();
            if( $pid > 0 ){
               // do nothing ...
            } else if( 0 == $pid ){
                echo "兒子".PHP_EOL;
                exit;
            }
        }

執行結果以下圖所示:

前面強調過:父進程和子進程將繼續執行fork以後的程序代碼。這裏就不解釋,實在想不明白的,能夠動手本身畫畫思考一下。

爲了不寫成臭尾理論文兒,這裏強行斷篇分割一下,下一章說殭屍進程和孤兒進程的一些恩怨情仇。

相關文章
相關標籤/搜索