>  기사  >  백엔드 개발  >  PHP 프로세스 간 통신에 대한 자세한 설명

PHP 프로세스 간 통신에 대한 자세한 설명

小云云
小云云원래의
2018-03-14 15:18:455208검색

프로세스는 특정 데이터 수집에 대해 독립적인 기능을 가진 프로그램의 실행 활동입니다. 즉, 시스템이 여러 CPU를 스케줄링할 때 프로그램의 기본 단위입니다. 프로세스는 대부분의 언어에 생소한 개념이 아니며 "세계 최고의 언어인 PHP"는 확실히 예외입니다.

Environment

PHP에서의 프로세스는 확장 기능의 형태로 완성됩니다. 이러한 확장을 통해 프로세스에서 일련의 작업을 쉽게 완료할 수 있습니다.

  • pcntl 확장: 메인 프로세스 확장, 완료 프로세스는 대기 작업에서 생성됩니다.

  • posix 확장: 프로세스 ID 가져오기, 프로세스 종료 등과 같은 POSIX 호환 시스템에 대한 전체 공통 API를 제공합니다.

  • sysvmsg 확장: system v 모드에서 프로세스 간 통신을 위한 메시지 대기열을 구현합니다.

  • sysvsem 확장: 시스템 v 세마포어를 구현합니다.

  • sysvshm 확장: 시스템 v 모드에서 공유 메모리를 구현합니다.

  • sockets 확장: 소켓 통신을 구현합니다.

이 확장 기능은 Linux/Mac에서만 사용할 수 있으며 Windows에서는 지원되지 않습니다. 마지막으로 PHP 버전은 5.5 이상을 권장합니다.

간단한 예

간단한 PHP 다중 프로세스 예에는 하나의 하위 프로세스와 하나의 상위 프로세스가 있습니다. 자식 프로세스는 5번의 출력을 내고 프로그램을 종료합니다.

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();
$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    $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 {
    // 父进程执行程序
    $childList[$pid] = 1;
}
// 等待子进程结束
pcntl_wait($status);
echo "({$parentPid})main progress end!";

완벽해요. 마침내 하위 프로세스와 상위 프로세스를 만들었습니다. 끝났나요? 아니요, 각 프로세스는 서로 독립적이고 교차점이 없으며 사용 범위가 심각하게 제한됩니다. 무엇을 해야 할까요?

4. IPC(프로세스 간 통신)

Linux에서 일반적으로 프로세스 통신 방법에는 메시지 큐, 세마포어, 공유 메모리, 신호, 파이프 및 소켓이 포함됩니다.

1. 메시지 큐

메시지 큐는 메모리에 저장되는 큐입니다. 다음 코드는 3개의 생산자 하위 프로세스와 2개의 소비자 하위 프로세스를 생성합니다. 이 5개 프로세스는 메시지 큐를 통해 통신합니다.

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";$childList = array();
// 创建消息队列,以及定义消息类型(类似于数据库中的库)
$id = ftok(__FILE__,&#39;m&#39;);
$msgQueue = msg_get_queue($id);
const MSG_TYPE = 1;
// 生产者
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);
    }
}
// 消费者
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) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress(&#39;producer&#39;);
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 2个写进程
for ($i = 0; $i < 2; $i ++ ) {
    $pid = createProgress(&#39;consumer&#39;);
    $childList[$pid] = 1;
    echo "create consumer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
echo "({$parentPid})main progress end!\n";

단 하나의 프로세스만 메시지 대기열의 데이터에 액세스할 수 있으므로 추가 잠금이나 세마포어가 필요하지 않습니다.

2. 세마포어와 공유 메모리

세마포어: 시스템이 제공하는 원자적 연산인 세마포어이며, 동시에 여러분의 프로세스만이 이를 연산할 수 있습니다. 프로세스가 세마포어를 획득하면 프로세스에서 이를 해제해야 합니다.

공유 메모리: 메모리 내에서 시스템이 공개하는 공통 메모리 영역으로, 모든 프로세스가 동시에 액세스할 수 있습니다. 데이터의 일관성을 보장하려면 메모리 영역이 필요합니다. 잠금 또는 세마포어가 됩니다.

아래에서는 메모리의 동일한 값을 수정하는 여러 프로세스를 만듭니다.

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();
 
// 创建共享内存,创建信号量,定义共享key
$shm_id = ftok(__FILE__,&#39;m&#39;);
$sem_id = ftok(__FILE__,&#39;s&#39;);
$shareMemory = shm_attach($shm_id);
$signal = sem_get($sem_id);
const SHARE_KEY = 1;
// 生产者
function producer(){
    global $shareMemory;
    global $signal;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        // 获得信号量
        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{
            // 无值,初始化
            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) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress(&#39;producer&#39;);
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
// 释放共享内存与信号量
shm_remove($shareMemory);
sem_remove($signal);
echo "({$parentPid})main progress end!\n";

3. 신호

신호는 시스템 호출입니다. 일반적으로 우리가 사용하는 kill 명령은 특정 프로세스에 특정 신호를 보내는 것입니다. liunx/mac에서 kill -l을 실행하여 특정 신호를 확인할 수 있습니다. 다음 예에서 상위 프로세스는 5초 동안 기다린 후 하위 프로세스에 siint 신호를 보냅니다. 하위 프로세스는 신호를 캡처하고 신호 처리 기능으로 처리합니다.

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
 
// 定义一个信号处理函数
function sighandler($signo) {
    $pid = posix_getpid();
    echo "{$pid} progress,oh no ,I&#39;m killed!\n";
    exit(1);
}
 
$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    // 注册信号处理函数
    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{
    // 父进程执行程序
    $childList[$pid] = 1;
    // 5秒后,父进程向子进程发送sigint信号.
    sleep(5);
    posix_kill($pid,SIGINT);
    sleep(5);
}
echo "({$parentPid})main progress end!\n";

4. 파이프(명명된 파이프)

파이프는 다중 프로세스 통신에 일반적으로 사용되는 수단입니다. 파이프는 명명되지 않은 파이프와 명명된 파이프로 구분되지만, 명명된 파이프는 관련 관계와의 프로세스 간 통신에만 사용할 수 있습니다. 유명한 파이프는 동일한 호스트의 모든 프로세스에서 사용할 수 있습니다. 이곳에서는 유명한 채널만 소개합니다. 다음 예에서는 하위 프로세스가 데이터를 쓰고 상위 프로세스가 데이터를 읽습니다.

// 定义管道路径,与创建管道
$pipe_path = &#39;/data/test.pipe&#39;;
if(!file_exists($pipe_path)){
    if(!posix_mkfifo($pipe_path,0664)){
        exit("create pipe error!");
    }
}
$pid = pcntl_fork();
if($pid == 0){
    // 子进程,向管道写数据
    $file = fopen($pipe_path,&#39;w&#39;);
    while (true){
        fwrite($file,&#39;hello world&#39;);
        $rand = rand(1,3);
        sleep($rand);
    }
    exit(&#39;child end!&#39;);
}else{
    // 父进程,从管道读数据
    $file = fopen($pipe_path,&#39;r&#39;);
    while (true){
        $rel = fread($file,20);
        echo "{$rel}\n";
        $rand = rand(1,2);
        sleep($rand);
    }
}

관련 권장 사항:

PHP 프로세스 잠금 구현 방법

PHP 프로세스 통신에 대한 참고 사항 요약

PHP 프로세스 통신을 기반으로 한 세마포 및 공유 메모리 통신

위 내용은 PHP 프로세스 간 통신에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.