>백엔드 개발 >PHP 튜토리얼 >php-fpm 다시 로드 프로세스

php-fpm 다시 로드 프로세스

藏色散人
藏色散人앞으로
2019-08-15 14:14:134016검색

요약: 다시 로드하면 절전 모드가 일찍 종료되므로 fpm의 다시 로드 작업을 구현하는 방법을 살펴보았습니다.

php-fpm reload process

이 글은 PHP7.0 fpm에서 분석되었으며, process_control_timeout이 0이 아닙니다.

Restart 신호

우선, fpm의 reload 작업이 실제로 USR2 신호를 fpm 프로세스에 보내는 것을 알 수 있습니다.

fpm의 마스터 프로세스에서 신호 처리 기능은 다음을 통해 등록됩니다.

int fpm_signals_init_main() /* {{{ */{ struct sigactionact;
  // 。。。。。。  memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler;
sigfillset(&act.sa_mask);
  if (0 > sigaction(SIGTERM,  &act, 0) ||
    0 > sigaction(SIGINT,  &act, 0) ||
    0 > sigaction(SIGUSR1,  &act, 0) ||
    0 > sigaction(SIGUSR2,  &act, 0) ||
    0 > sigaction(SIGCHLD,  &act, 0) ||
    0 > sigaction(SIGQUIT,  &act, 0)) {
zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()"); return -1;
} return 0;
}/* }}} */

간단히 를 통해 모든 신호를 차단하도록 설정한 다음 sigaction을 통해 해당 신호 처리 기능을 설정합니다.

fpm을 다시 로드하면 systemctl은 USR2 신호를 fpm의 마스터 프로세스에 보내고 다음 함수를 실행합니다.

static void sig_handler(int signo) /* {{{ */{ static const char sig_chars[NSIG + 1] = {
[SIGTERM] = 'T',
[SIGINT]  = 'I',
[SIGUSR1] = '1',
[SIGUSR2] = '2',
[SIGQUIT] = 'Q',
[SIGCHLD] = 'C'
}; char s;
        // ***
s = sig_chars[signo];
zend_quiet_write(sp[1], &s, sizeof(s));
errno = saved_errno;
}/* }}} */

핵심은 zend_quiet_write입니다. sig_handler 함수는 문자열 2를 sp[1]에 씁니다.

여기서 sp[0] 및 sp[1]은 를 통해 생성된 로컬 소켓이라는 점에 유의해야 합니다.

마스터가 다시 시작됩니다. 신호가 발생하면 이전 신호 처리 기능이 호출되지만 프로그램의 기본 논리는 여전히 중단되지 않습니다. 그러면 fpm 마스터 프로세스는 다시 로드되는 것을 어떻게 알 수 있습니까?

답은 마스터 프로세스의 이벤트 루프인 에 있습니다.

루핑하기 전에 sp[0]을 사용하여 fpm_event_s 구조체를 청취 fd에 추가해야 합니다.

int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */{ if (!ev || !callback || fd < -1) { return -1;
}
memset(ev, 0, sizeof(struct fpm_event_s));
ev->fd = fd;
ev->callback = callback;
ev->arg = arg;
ev->flags = flags; return 0;
}/* }}} */

그런 다음 코드의 ev인 fpm_event_s 구조체를 청취 fd에 추가합니다.

사실 이 추가 프로세스는 fpm의 다양한 비동기 모델과도 관련이 있습니다(모두 fpm_event_module_s에 해당하는 add 메소드로 구현됨). 예를 들어 전체 ev 매개변수는 epoll_event의 data.ptr에 배치됩니다. (설문 추가에 대해서는 를 참조하세요.)

모든 fd가 추가된 후(물론 신호 관련 fd뿐만 아니라) 이벤트가 오기를 기다리는 데 사용할 수 있습니다. (epoll과 poll도 각각 wait 메소드를 구현합니다.)

좋아요, sig_handler로 돌아가서 문자열 2를 sp[1]에 씁니다. wait 메소드는 신호를 수신하고 해당 ev를 가져와 호출합니다. 실제로는 다음과 같이 호출됩니다.

static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */{ char c; int res, ret; int fd = ev->fd;
  do {
res = read(fd, &c, 1);
  switch (c) { // 。。。。。。
case &#39;2&#39; :                  /* SIGUSR2 */
zlog(ZLOG_DEBUG, "received SIGUSR2");
zlog(ZLOG_NOTICE, "Reloading in progress ...");
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET); break;
}
  if (fpm_globals.is_child) { break;
}
} while (1); return;
}/* }}} */

문자열 2를 수신하면

fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET)

를 실행합니다. 실제로는 다음과 같습니다.

void fpm_pctl(int new_state, int action) /* {{{ */{ switch (action) { case FPM_PCTL_ACTION_SET :
//。。。。。。
fpm_signal_sent = 0;
fpm_state = new_state;
zlog(ZLOG_DEBUG, "switching to &#39;%s&#39; state", fpm_state_names[fpm_state]); /* fall down */  case FPM_PCTL_ACTION_TIMEOUT :
fpm_pctl_action_next(); break; //。。。。。
}
}/* }}} */

즉, fpm_state를 FPM_PCTL_STATE_RELOADING 이후로 설정하면 중단이 없고 실행이 계속됩니다.

static void fpm_pctl_action_next() /* {{{ */
{ int sig, timeout;
  if (!fpm_globals.running_children) {
fpm_pctl_action_last();
}
  if (fpm_signal_sent == 0) { if (fpm_state == FPM_PCTL_STATE_TERMINATING) { sig = SIGTERM;
} else { sig = SIGQUIT;
}
timeout = fpm_global_config.process_control_timeout;
} else { if (fpm_signal_sent == SIGQUIT) { sig = SIGTERM;
} else { sig = SIGKILL;
}
timeout = 1;
}
fpm_pctl_kill_all(sig);
fpm_signal_sent = sig;
fpm_pctl_timeout_set(timeout);
}
/* }}} */

즉, SIGQUIT 신호가 모든 하위 프로세스로 전송됩니다.

여기에 또 다른 것이 있는데, 이에 대해서는 나중에 논의하겠습니다.

자식 프로세스가 신호를 처리합니다. 상위 프로세스가 신호 전송을 마친 후에는 하위 프로세스가 이를 처리할 차례입니다.

하위 프로세스는 처리를 위해 sig_soft_quit에만 넘겨질 수 있습니다. 하위 프로세스가 초기화된 후 SIGQUIT 신호가 수신되고 sig_soft_quit에 의해 처리됩니다. 최종 호출 처리:

void fcgi_terminate(void){
in_shutdown = 1;
}

는 in_shutdown을 1로 설정하는 것입니다.

하위 프로세스가 종료됩니다. 하위 프로세스의 루프 본문은 in_shutdown에 더 많은 판단이 포함된 fcgi_accept_request에 있습니다. 1이면 바로 종료됩니다.

Timeout 처리는 앞서 언급했습니다. 다음 작업이 수행되었습니다.

fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);

이 조건에서 은(는) 하위 프로세스를 직접 종료했습니다.

수면이 방해되는 이유는 무엇인가요? 시스템이 sleep을 호출하는 것을 볼 수 있습니다(php_sleep은 sleep의 매크로입니다):

/* {{{ proto void sleep(int seconds)
   Delay for a given number of seconds */PHP_FUNCTION(sleep)
{
zend_longnum;
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) {
RETURN_FALSE;
}
php_sleep((unsigned int)num);
}/* }}} */

sleep 함수가 실행되면 이때 프로세스의 상태는 S입니다.

interruptiblesleep 이때 신호가 트리거되면, 신호는 우리의 것과 같이 즉시 처리됩니다. 방금 언급한 SIGQUIT 이후에 절전 모드가 완료되었음을 알았습니다.

내가 썼기 때문에:

<b>sleep</b>() makesthecallingthreadsleepuntil <i>seconds</i> secondshave 
elapsedor a signalarriveswhichis not ignored.

주의해야 할 점은 신호가 절전 모드를 중단하더라도 절전 모드를 건너뛰고 실행을 계속한다는 것입니다.

위 내용은 php-fpm 다시 로드 프로세스의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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