溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PHP中的進(jìn)程和進(jìn)程間通信是什么

發(fā)布時(shí)間:2021-06-23 10:08:58 來(lái)源:億速云 閱讀:143 作者:chen 欄目:編程語(yǔ)言

本篇內(nèi)容主要講解“PHP中的進(jìn)程和進(jìn)程間通信是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“PHP中的進(jìn)程和進(jìn)程間通信是什么”吧!

環(huán)境

php中的進(jìn)程是以擴(kuò)展的形式來(lái)完成。通過(guò)這些擴(kuò)展,我們能夠很輕松的完成進(jìn)程的一系列動(dòng)作。

pcntl擴(kuò)展:主要的進(jìn)程擴(kuò)展,完成進(jìn)程創(chuàng)建于等待操作。 posix擴(kuò)展:完成posix兼容機(jī)通用api,如獲取進(jìn)程id,殺死進(jìn)程等。 sysvmsg擴(kuò)展:實(shí)現(xiàn)system v方式的進(jìn)程間通信之消息隊(duì)列。 sysvsem擴(kuò)展:實(shí)現(xiàn)system v方式的信號(hào)量。 sysvshm擴(kuò)展:實(shí)現(xiàn)system v方式的共享內(nèi)存。 sockets擴(kuò)展:實(shí)現(xiàn)socket通信。 這些擴(kuò)展只能在linux/mac中使用,window下是不支持。最后建議php版本為5.5+。

簡(jiǎn)單的例子

一個(gè)簡(jiǎn)單的PHP多進(jìn)程例子,該例子中,一個(gè)子進(jìn)程,一個(gè)父進(jìn)程。子進(jìn)程輸出5次,退出程序。

echo "parent progress pid:{$parentPid}\n";

$childList = array();

$pid = pcntl_fork();

if ( $pid == -1) {

    // 創(chuàng)建失敗

    exit("fork progress error!\n");

} else if ($pid == 0) {

    // 子進(jìn)程執(zhí)行程序

    $pid = posix_getpid();

    $repeatNum = 5;

    for ( $i = 1; $i <= $repeatNum; $i++) {

        echo "({$pid})child progress is running! {$i} \n";

        $rand = rand(1,3);

        sleep($rand);

    }

    exit("({$pid})child progress end!\n");

} else {

    // 父進(jìn)程執(zhí)行程序

    $childList[$pid] = 1;

}

// 等待子進(jìn)程結(jié)束

pcntl_wait($status);
echo "({$parentPid})main progress end!";

完美,終于創(chuàng)建了一個(gè)子進(jìn)程,一個(gè)父進(jìn)程。完了么?沒有,各個(gè)進(jìn)程之間相互獨(dú)立的,沒有任何交集,使用范圍嚴(yán)重受到現(xiàn)在。怎么辦,哪就進(jìn)程間通信(interprogress communication)唄。

進(jìn)程間通信(IPC)

通常linux中的進(jìn)程通信方式有:消息隊(duì)列、信號(hào)量、共享內(nèi)存、信號(hào)、管道、socket。

1.消息隊(duì)列

消息隊(duì)列是存放在內(nèi)存中的一個(gè)隊(duì)列。如下代碼將創(chuàng)建3個(gè)生產(chǎn)者子進(jìn)程,2個(gè)消費(fèi)者子進(jìn)程。這5個(gè)進(jìn)程將通過(guò)消息隊(duì)列通信。

echo "parent progress pid:{$parentPid}\n";$childList = array();
// 創(chuàng)建消息隊(duì)列,以及定義消息類型(類似于數(shù)據(jù)庫(kù)中的庫(kù))
$id = ftok(__FILE__,'m');
$msgQueue = msg_get_queue($id);
const MSG_TYPE = 1;
// 生產(chǎn)者
function producer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $str = "({$pid})progress create! {$i}";
        msg_send($msgQueue,MSG_TYPE,$str);
        $rand = rand(1,3);
        sleep($rand);
    }
}
// 消費(fèi)者
function consumer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 6;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $rel = msg_receive($msgQueue,MSG_TYPE,$msgType,1024,$message);
        echo "{$message} | consumer({$pid}) destroy \n";
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 創(chuàng)建失敗
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子進(jìn)程執(zhí)行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父進(jìn)程執(zhí)行程序
        return $pid;
    }
}
// 3個(gè)寫進(jìn)程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress('producer');
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 2個(gè)寫進(jìn)程
for ($i = 0; $i < 2; $i ++ ) {
    $pid = createProgress('consumer');
    $childList[$pid] = 1;
    echo "create consumer child progress: {$pid} \n";
}
// 等待所有子進(jìn)程結(jié)束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
echo "({$parentPid})main progress end!\n";

由于消息隊(duì)列去數(shù)據(jù)是,只有一個(gè)進(jìn)程能去到,所以不需要額外的鎖或信號(hào)量。

2. 信號(hào)量與共享內(nèi)存

信號(hào)量:是系統(tǒng)提供的一種原子操作,一個(gè)信號(hào)量,同時(shí)只有你個(gè)進(jìn)程能操作。一個(gè)進(jìn)程獲得了某個(gè)信號(hào)量,就必須被該進(jìn)程釋放掉。

共享內(nèi)存:是系統(tǒng)在內(nèi)存中開辟的一塊公共的內(nèi)存區(qū)域,任何一個(gè)進(jìn)程都可以訪問,在同一時(shí)刻,可以有多個(gè)進(jìn)程訪問該區(qū)域,為了保證數(shù)據(jù)的一致性,需要對(duì)該內(nèi)存區(qū)域加鎖或信號(hào)量。

以下,創(chuàng)建多個(gè)進(jìn)程修改內(nèi)存中的同一個(gè)值。

echo "parent progress pid:{$parentPid}\n";
$childList = array();

// 創(chuàng)建共享內(nèi)存,創(chuàng)建信號(hào)量,定義共享key
$shm_id = ftok(__FILE__,'m');
$sem_id = ftok(__FILE__,'s');
$shareMemory = shm_attach($shm_id);
$signal = sem_get($sem_id);
const SHARE_KEY = 1;
// 生產(chǎn)者
function producer(){
    global $shareMemory;
    global $signal;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        // 獲得信號(hào)量
        sem_acquire($signal);
        
        if (shm_has_var($shareMemory,SHARE_KEY)){
            // 有值,加一
            $count = shm_get_var($shareMemory,SHARE_KEY);
            $count ++;
            shm_put_var($shareMemory,SHARE_KEY,$count);
            echo "({$pid}) count: {$count}\n";
        }else{
            // 無(wú)值,初始化
            shm_put_var($shareMemory,SHARE_KEY,0);
            echo "({$pid}) count: 0\n";
        }
        // 用完釋放
        sem_release($signal);
        
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 創(chuàng)建失敗
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子進(jìn)程執(zhí)行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父進(jìn)程執(zhí)行程序
        return $pid;
    }
}
// 3個(gè)寫進(jìn)程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress('producer');
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 等待所有子進(jìn)程結(jié)束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
// 釋放共享內(nèi)存與信號(hào)量
shm_remove($shareMemory);
sem_remove($signal);
echo "({$parentPid})main progress end!\n";

3.信號(hào)

信號(hào)是一種系統(tǒng)調(diào)用。通常我們用的kill命令就是發(fā)送某個(gè)信號(hào)給某個(gè)進(jìn)程的。具體有哪些信號(hào)可以在liunx/mac中運(yùn)行kill -l查看。下面這個(gè)例子中,父進(jìn)程等待5秒鐘,向子進(jìn)程發(fā)送sigint信號(hào)。子進(jìn)程捕獲信號(hào),掉信號(hào)處理函數(shù)處理。

echo "parent progress pid:{$parentPid}\n";

// 定義一個(gè)信號(hào)處理函數(shù)
function sighandler($signo) {
    $pid = posix_getpid();
    echo "{$pid} progress,oh no ,I'm killed!\n";
    exit(1);
}

$pid = pcntl_fork();
if ( $pid == -1) {
    // 創(chuàng)建失敗
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子進(jìn)程執(zhí)行程序
    // 注冊(cè)信號(hào)處理函數(shù)
    declare(ticks=10);
    pcntl_signal(SIGINT, "sighandler");
    $pid = posix_getpid();
    while(true){
        echo "{$pid} child progress is running!\n";
        sleep(1);
    }
    exit("({$pid})child progress end!\n");
}else{
    // 父進(jìn)程執(zhí)行程序
    $childList[$pid] = 1;
    // 5秒后,父進(jìn)程向子進(jìn)程發(fā)送sigint信號(hào).
    sleep(5);
    posix_kill($pid,SIGINT);
    sleep(5);
}
echo "({$parentPid})main progress end!\n";

4.管道(有名管道)

管道是比較常用的多進(jìn)程通信手段,管道分為無(wú)名管道與有名管道,無(wú)名管道只能用于具有親緣關(guān)系的進(jìn)程間通信,而有名管道可以用于同一主機(jī)上任意進(jìn)程。這里只介紹有名管道。下面的例子,子進(jìn)程寫入數(shù)據(jù),父進(jìn)程讀取數(shù)據(jù)。

$pipe_path = '/data/test.pipe';
if(!file_exists($pipe_path)){
    if(!posix_mkfifo($pipe_path,0664)){
        exit("create pipe error!");
    }
}
$pid = pcntl_fork();
if($pid == 0){
    // 子進(jìn)程,向管道寫數(shù)據(jù)
    $file = fopen($pipe_path,'w');
    while (true){
        fwrite($file,'hello world');
        $rand = rand(1,3);
        sleep($rand);
    }
    exit('child end!');
}else{
    // 父進(jìn)程,從管道讀數(shù)據(jù)
    $file = fopen($pipe_path,'r');
    while (true){
        $rel = fread($file,20);
        echo "{$rel}\n";
        $rand = rand(1,2);
        sleep($rand);
    }
}

到此,相信大家對(duì)“PHP中的進(jìn)程和進(jìn)程間通信是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI