>  기사  >  백엔드 개발  >  하나의 기사로 PHP 프로세스 신호 처리에 대한 철저한 이해

하나의 기사로 PHP 프로세스 신호 처리에 대한 철저한 이해

藏色散人
藏色散人앞으로
2023-04-01 07:30:031961검색

이 기사는 PHP 프로세스 신호 처리를 중심으로 자세히 소개하는 PHP 관련 지식을 제공합니다. 관심 있는 친구들이 함께 살펴보는 것이 모든 사람에게 도움이 되기를 바랍니다.

하나의 기사로 PHP 프로세스 신호 처리에 대한 철저한 이해

Background

2주 전에 상사가 나에게 신호 모니터링 패키지를 작성하라는 임무를 부여했습니다. 우리 회사의 프로젝트는 컨테이너에서 실행되기 때문에 온라인에 접속할 때마다 이미지를 다시 패키징한 다음 시작해야 합니다. 다시 패키징하기 전에 Dokcer는 먼저 컨테이너에 신호를 보낸 다음 SIGKILL 신호를 보내기 전에 제한 시간(기본값 10초) 동안 대기합니다. 이제 컨테이너에 상주 프로세스가 있는 상황이 발생합니다. 프로세스의 작업은 대기열의 메시지를 지속적으로 소비하는 것입니다. 지금 온라인 상태로 전환하고 컨테이너를 종료해야 한다고 가정하면 Docker는 컨테이너에서 실행 중인 상주 프로세스에 신호를 보내 10초 후에 종료하겠다고 알려줍니다. 메시지의 경우 1초 이내에 후속 로직이 실행되기 전에 프로세스가 종료되어 메시지가 손실되고 더티 데이터가 생성될 수 있습니다. 신호로 모니터링해야 하는 이 작업의 배경을 결정합니다. 위의 상황에서 상주 프로세스가 Docker가 보낸 종료 신호를 받으면 프로세스가 차단되고 컨테이너가 종료될 때까지 절전 모드로 들어갈 수 있습니다. 자, 배경을 명확히 한 후 PHP의 신호를 소개하겠습니다. (나중에 이 패키지를 작성하는 방법에 대한 기사를 편집하고 필요한 친구들이 사용할 수 있도록 패키지를 https://packagist.org/에 게시할 예정입니다.) [ 추천 학습 :

PHP 비디오 튜토리얼

]

1. Linux 운영 체제에는

소프트웨어 인터럽트라고도 하는 어떤 신호가 있습니까? 예를 들어, 자식 프로세스가 종료되면 프로세스는 SIGCHLD(신호 번호 17)를 부모 프로세스에 보내 부모 프로세스에 알리므로 신호는 상호 프로세스로도 사용됩니다. 프로세스 통신 메커니즘. Linux 시스템에서는 일반적으로 프로세스를 종료하기 위해 kill -9 XXPID를 사용합니다. 실제로 이 명령의 핵심은 SIGKILL(신호 번호 9)을 프로세스에 보내는 것입니다. 종료하려면 Ctrl+C 단축키를 사용하세요. 실행, 이 단축키의 핵심은 SIGINT(신호 2번)를 현재 프로세스에 보내는 것이며, 이 신호를 받을 때 프로세스의 기본 동작은 작업을 종료하는 것입니다.

2. 일반적인 신호

다음 신호를 볼 수 있습니다. 다음은 중요하고 일반적으로 사용되는 신호입니다.

신호 이름 신호 값 신호 유형 신호 설명
SIGHUP 1 프로세스 종료(단말선 끊기) 이 신호 사용자 단말기에 연결되어 있습니다(정상 여부) 정상적으로 발행되고 마지막에 일반적으로 터미널의 제어 프로세스가 종료될 때 동일한 세션의 각 작업을 알리며 더 이상 제어 터미널과 연결되지 않습니다
SIGQUIT 2 종료합니다. 프로세스(프로세스 중단) 프로그램 종료(인터럽트, 신호, 사용자가 INTR 문자를 입력할 때 발생(보통 Ctrl-C,
SIGQUIT 3 CORE 파일을 생성하여 프로세스를 종료하고 생성) CORE 파일 프로세스 및 CORE 파일 생성 SIGQUIT는 SIGINT와 유사하지만 QUIT 문자(보통 Ctrl-,)에 의해 제어됩니다. SIGQUIT를 수신하여 프로세스가 종료되면 코어 파일이 생성됩니다. 센스는 프로그램 오류 신호와 비슷합니다
SIGFPE 8 CORE 파일 생성(부동 소수점 예외) SIGFPE는 치명적인 산술 연산 오류가 발생한 경우에 발생합니다. 또한 오버플로 및 0
SIGKILL 9 프로세스 종료(프로세스 종료) SIGKILL은 프로그램 실행을 즉시 종료하는 데 사용됩니다. 이 신호는 차단할 수 없습니다. , 처리 및 무시됨
SIGSEGV 11
SIGSEGV가 자체 메모리에 할당되지 않은 항목에 액세스하려고 시도하거나 쓰기 권한이 없는 메모리 주소에 데이터를 쓰려고 시도함
SIGALRM 14 프로세스 종료(타이머 만료) SIGALRM 시계 타이밍 신호, 실제 시간 또는 시계 시간 알람 이 함수는 이 신호를 사용하여
SIGTERM 15 ) SIGTERM 프로그램 종료(종료, 신호, SIGKILL과 달리 이 신호는 차단 및 처리될 수 있습니다. 일반적으로 프로그램 자체를 정상적으로 종료하도록 요청하는 데 사용됩니다. 쉘 명령 kill은 기본적으로 이 신호를 생성합니다
SIGCHLD 17 신호를 무시합니다(자식 프로세스가 중지되거나 종료되면 상위 프로세스에 알립니다) SIGCHLD 하위 프로세스가 끝나면 상위 프로세스는 이 신호를 받습니다.
SIGVTALRM 26 프로세스 종료(가상 타이머 만료) SIGVTALRM 가상 클럭 신호. SIGALRM과 유사하지만 프로세스가 차지하는 CPU 시간을 계산합니다
SIGIO 29 신호를 무시합니다(설명자에서 I/O가 가능함) SIGIO 파일 설명자가 준비되었으며 입출력 작업을 시작할 수 있습니다

2. PHP의 신호 처리 관련 기능

PHP의 pcntl 확장 및 posix 확장은 신호를 작동하는 여러 방법을 제공합니다. (이러한 기능을 사용하려면 먼저 이러한 기능을 설치해야 합니다. Extension)

다음은 이 작업에서 사용한 여러 방법에 대한 자세한 소개입니다.

declare

선언 구조는 코드 조각의 실행 지침을 설정하는 데 사용됩니다. 선언 구문은 다른 흐름 제어 구조와 유사합니다. 지시문 부분을 사용하면 선언 코드 세그먼트의 동작을 설정할 수 있습니다. 현재

ticks 및 인코딩

이라는 두 가지 명령만 인식됩니다. 선언 코드 세그먼트의 명령문 부분이 실행됩니다. 실행 방법과 실행 중에 발생하는 부작용은 지시문에 설정된 명령에 따라 다릅니다.

Ticks

Tick(시계 주기)은 선언 코드의 해석기입니다. 세그먼트 N 시간의

저수준 명령문

이 실행될 때마다 발생하는 이벤트 N의 값은 선언의 지시문 부분에 Ticks=N으로 지정됩니다. 모든 진술에 시간을 정할 수 있는 것은 아닙니다. 일반적으로 조건식매개변수 식은 모두 시간 제한이 없습니다. 각 틱에 나타나는 이벤트는 Register_tick_function()에 의해 지정됩니다. 각 틱에 여러 이벤트가 나타날 수 있습니다. 자세한 내용은 공식 문서를 참조하세요: https://www.php.net/manual/zh/control-structures.declare.php

declare (directive)
    statement

pcntl_signalpcntl_signal, 신호 프로세서 설치

<?php
declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数
$a=1;//在注册之前,不算
function test(){//定义一个函数
    echo "执行\n";
}
register_tick_function(&#39;test&#39;);//该条注册函数会被当成低级语句被执行
for($i=0;$i<=2;$i++){//for算一条低级语句
    $i=$i;//赋值算一条
}
输出:六个“执行”

pcntl_signal() 함수는 signo

pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool

posix_killposix_kill에 의해 지정된 신호에 대한 새 신호 프로세서를 설치하고 프로세스에 신호를 보냅니다

declare(ticks = 1);
pcntl_signal(SIGINT,function(){
    echo "你按了Ctrl+C".PHP_EOL;
});
while(1){
    sleep(1);//死循环运行低级语句
}
输出:当按Ctrl+C之后,会输出“你按了Ctrl+C”

첫 번째 매개변수는 프로세스 ID입니다. 첫 번째 매개변수는 프로세스 ID입니다

posix_kill ( int $pid , int $sig ) : bool

pcntl_signal_dispatchpcntl_signal_dispatch는 신호를 기다리는 프로세서를 호출합니다

a.php
<?php
declare(ticks = 1);
echo getmypid();//获取当前进程id
pcntl_signal(SIGINT,function(){
    echo "你给我发了SIGINT信号";
});
while(1){
    sleep(1);
}

b.php
<?php
posix_kill(执行1.php时输出的进程id, SIGINT);

함수 pcntl_signal_dispatch()가 호출됩니다. pcntl_signal()

pcntl_signal_dispatch ( void ) : bool

pcntl_async_signals()비동기 신호 처리를 통해 각 대기 신호에 의해 설치된 프로세서. 틱(추가 오버헤드가 많이 발생함) 없이 비동기 신호 처리를 가능하게 하는 데 사용됩니다. (PHP>=7.1)

<?php
echo "安装信号处理器...\n";
pcntl_signal(SIGHUP,  function($signo) {
     echo "信号处理器被调用\n";
});
echo "为自己生成SIGHUP信号...\n";
posix_kill(posix_getpid(), SIGHUP);
echo "分发...\n";
pcntl_signal_dispatch();
echo "完成\n";
?>

输出:
安装信号处理器...
为自己生成SIGHUP信号...
分发...
信号处理器被调用
完成

3. PHP에서 세마포어를 처리하는 방법 앞서 선언(ticks=1)과 pcntl_signal의 조합을 통해 신호를 모니터링할 수 있다는 것을 알고 있습니다. 각 PHP 저수준 명령문은 현재 프로세스에 처리되지 않은 신호가 있는지 확인합니다. 이는 실제로 성능을 많이 소모합니다.

pcntl_signal의 구현 원리는 신호를 트리거한 후 먼저 신호를 대기열에 추가하는 것입니다. 그런 다음 PHP의 틱 콜백 함수에 신호가 있는지 지속적으로 확인하십시오. 신호가 있으면 PHP에 지정된 콜백 함수를 실행하십시오.

<?php
pcntl_async_signals(true); // turn on async signals

pcntl_signal(SIGHUP,  function($sig) {
    echo "SIGHUP\n";
});

posix_kill(posix_getpid(), SIGHUP);

输出:
SIGHUP

PHP5.3 이후에는 pcntl_signal_dispatch 함수가 있습니다. 현재로서는 선언이 더 이상 필요하지 않습니다. 신호를 호출하고 전달하려면 루프에 함수를 추가하기만 하면 됩니다.

PHP_MINIT_FUNCTION(pcntl)
{
 php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_add_tick_function(pcntl_signal_dispatch TSRMLS_CC);

 return SUCCESS;
}

PHP의 Ticks=1은 이 함수가 한 줄에 호출될 때마다 호출된다는 것을 모두가 알고 있습니다. PHP 코드가 실행됩니다. 실제로 대부분의 경우 신호가 생성되지 않지만 Ticks 기능은 항상 실행됩니다. 서버 프로그램이 1초에 1,000개의 요청을 받으면 평균적으로 각 요청은 1,000줄의 PHP 코드를 실행합니다. 그런 다음 PHP의 pcntl_signal은 추가로 1000 * 1000을 가져옵니다. 이는 100만 번의 빈 함수 호출입니다. 이렇게 하면 CPU 리소스가 많이 낭비됩니다. 더 나은 접근 방식은 틱을 제거하고 대신 pcntl_signal_dispatch를 사용하여 코드 루프에서 직접 신호를 처리하는 것입니다. pcntl_signal_dispatch 함수 구현:

<?php
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
    echo "触发信号用户自定义信号1";
});
while(1){
    pcntl_signal_dispatch();
    sleep(1);//死循环运行低级语句
}

하지만 위 함수의 역겨운 점은 무한 루프에 배치해야 한다는 것입니다. PHP7.1 이후에는 비동기 신호 수신 및 처리를 완료하는 함수가 나왔습니다. pcntl_async_signals

void pcntl_signal_dispatch()
{
 //.... 这里略去一部分代码,queue即是信号队列
 while (queue) {
  if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
   ZVAL_NULL(&retval);
   ZVAL_LONG(&param, queue->signo);

   /* Call php signal handler - Note that we do not report errors, and we ignore the return value */
   /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
   call_user_function(EG(function_table), NULL, handle, &retval, 1, &param TSRMLS_CC);
   zval_ptr_dtor(&param);
   zval_ptr_dtor(&retval);
  }
  next = queue->next;
  queue->next = PCNTL_G(spares);
  PCNTL_G(spares) = queue;
  queue = next;
 }
}

pcntl_async_signals 메서드를 사용하면 무한 루프를 작성할 필요가 없습니다.

监听信号的包:

https://github.com/Rain-Life/monitorSignal

위 내용은 하나의 기사로 PHP 프로세스 신호 처리에 대한 철저한 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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