이 기사는 PHP 프로세스 신호 처리를 중심으로 자세히 소개하는 PHP 관련 지식을 제공합니다. 관심 있는 친구들이 함께 살펴보는 것이 모든 사람에게 도움이 되기를 바랍니다.
2주 전에 상사가 나에게 신호 모니터링 패키지를 작성하라는 임무를 부여했습니다. 우리 회사의 프로젝트는 컨테이너에서 실행되기 때문에 온라인에 접속할 때마다 이미지를 다시 패키징한 다음 시작해야 합니다. 다시 패키징하기 전에 Dokcer는 먼저 컨테이너에 신호를 보낸 다음 SIGKILL 신호를 보내기 전에 제한 시간(기본값 10초) 동안 대기합니다. 이제 컨테이너에 상주 프로세스가 있는 상황이 발생합니다. 프로세스의 작업은 대기열의 메시지를 지속적으로 소비하는 것입니다. 지금 온라인 상태로 전환하고 컨테이너를 종료해야 한다고 가정하면 Docker는 컨테이너에서 실행 중인 상주 프로세스에 신호를 보내 10초 후에 종료하겠다고 알려줍니다. 메시지의 경우 1초 이내에 후속 로직이 실행되기 전에 프로세스가 종료되어 메시지가 손실되고 더티 데이터가 생성될 수 있습니다. 신호로 모니터링해야 하는 이 작업의 배경을 결정합니다. 위의 상황에서 상주 프로세스가 Docker가 보낸 종료 신호를 받으면 프로세스가 차단되고 컨테이너가 종료될 때까지 절전 모드로 들어갈 수 있습니다. 자, 배경을 명확히 한 후 PHP의 신호를 소개하겠습니다. (나중에 이 패키지를 작성하는 방법에 대한 기사를 편집하고 필요한 친구들이 사용할 수 있도록 패키지를 https://packagist.org/에 게시할 예정입니다.) [ 추천 학습 :
PHP 비디오 튜토리얼]
1. Linux 운영 체제에는신호 이름 | 신호 값 | 신호 유형 | 신호 설명 |
---|---|---|---|
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 파일 설명자가 준비되었으며 입출력 작업을 시작할 수 있습니다 |
PHP의 pcntl 확장 및 posix 확장은 신호를 작동하는 여러 방법을 제공합니다. (이러한 기능을 사용하려면 먼저 이러한 기능을 설치해야 합니다. Extension)
다음은 이 작업에서 사용한 여러 방법에 대한 자세한 소개입니다.
선언 구조는 코드 조각의 실행 지침을 설정하는 데 사용됩니다. 선언 구문은 다른 흐름 제어 구조와 유사합니다. 지시문 부분을 사용하면 선언 코드 세그먼트의 동작을 설정할 수 있습니다. 현재
ticks 및 인코딩이라는 두 가지 명령만 인식됩니다. 선언 코드 세그먼트의 명령문 부분이 실행됩니다. 실행 방법과 실행 중에 발생하는 부작용은 지시문에 설정된 명령에 따라 다릅니다.
TicksTick(시계 주기)은 선언 코드의 해석기입니다. 세그먼트 N 시간의
저수준 명령문이 실행될 때마다 발생하는 이벤트 N의 값은 선언의 지시문 부분에 Ticks=N으로 지정됩니다. 모든 진술에 시간을 정할 수 있는 것은 아닙니다. 일반적으로 조건식과 매개변수 식은 모두 시간 제한이 없습니다. 각 틱에 나타나는 이벤트는 Register_tick_function()에 의해 지정됩니다. 각 틱에 여러 이벤트가 나타날 수 있습니다.
자세한 내용은 공식 문서를 참조하세요: https://www.php.net/manual/zh/control-structures.declare.phpdeclare (directive)
statement
<?php declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数 $a=1;//在注册之前,不算 function test(){//定义一个函数 echo "执行\n"; } register_tick_function('test');//该条注册函数会被当成低级语句被执行 for($i=0;$i<=2;$i++){//for算一条低级语句 $i=$i;//赋值算一条 } 输出:六个“执行”
pcntl_signal() 함수는 signo
pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool
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
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
<?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信号... 分发... 信号处理器被调用 完成
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(¶m, 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, ¶m TSRMLS_CC); zval_ptr_dtor(¶m); 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!