>  기사  >  백엔드 개발  >  한 기사에서 PHP의 프로세스와 프로세스 간 통신에 대해 알아보세요.

한 기사에서 PHP의 프로세스와 프로세스 간 통신에 대해 알아보세요.

青灯夜游
青灯夜游앞으로
2021-06-22 18:34:462780검색

이 기사에서는 PHP의 프로세스와 프로세스 간 통신을 안내합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

한 기사에서 PHP의 프로세스와 프로세스 간 통신에 대해 알아보세요.

Environment

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

pcntl 확장: 메인 프로세스 확장, 완료 프로세스는 대기 작업에서 생성됩니다. posix 확장: 프로세스 ID 가져오기, 프로세스 종료 등과 같은 완전한 posix 호환 시스템 일반 API입니다. sysvmsg 확장: System v 모드에서 프로세스 간 통신을 구현하는 메시지 큐입니다. sysvsem 확장: system v 모드에서 세마포어를 구현합니다. sysvshm 확장: System v 모드에서 공유 메모리를 구현합니다. 소켓 확장: 소켓 통신을 구현합니다. 이러한 확장은 linux/mac에서만 사용할 수 있으며 Windows에서는 지원되지 않습니다. 마지막으로 PHP 버전은 5.5 이상을 권장합니다.

간단한 예

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

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!";

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

프로세스 간 통신(IPC)

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

1. 메시지 큐

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

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. 세마포어와 공유 메모리

세마포어: 시스템에서 제공하는 원자적 연산인 세마포어를 사용하며 동시에 실행할 수 있는 프로세스는 다음과 같습니다. 프로세스가 세마포어를 획득하면 프로세스에서 이를 해제해야 합니다.

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

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

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 신호를 보냅니다. 하위 프로세스는 신호를 캡처하고 신호 처리 기능으로 처리합니다.

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제