[nginx] nginx 소개 - 신호 처리

nginx 는 master 프로 세 스 와 worker 프로 세 스 를 통 해 서 비 스 를 제공 하 는 모드 입 니 다.master 프로 세 스 는 주로 worker 프로 세 스 상 태 를 감청 하여 재 부팅, 정지 등 작업 을 하 는 데 사 용 됩 니 다.worker 프로 세 스 는 구체 적 인 서 비 스 를 제공 하고 요청 을 받 아들 이 며 처리 합 니 다.마스터 가 워 커 프로 세 스 를 감청 한 다 는 말 이 나 온 이상 프로 세 스 간 통신 을 사용 해 야 합 니 다.일반적인 프로 세 스 간 통신 도 메모리, 메시지 큐, socke, 신호 등 몇 가지 방식 이 아 닙 니 다.nginx 에 서 는 공유 메모리, socket, 신호 세 가지 방식 을 사용 합 니 다.master 와 worker 프로 세 스 사이 의 통신 은 소켓 을 통 해 맞 추 는 방식 입 니 다.우리 와 nginx 간 의 통신 방식 은 신호 이다.nginx 를 사용 하 는 과정 에서 우리 가 직접 통신 할 수 있 는 것 은 master 프로 세 스 뿐 입 니 다. worker 프로 세 스 에 대해 서 는 그 개 수 를 결정 할 수 있 는 것 외 에 worker 프로 세 스 를 직접 제어 할 수 없습니다.워 커 프로 세 스 를 제어 하 는 유일한 방법 은 master 프로 세 스에 해당 하 는 신 호 를 보 내 는 것 입 니 다. master 프로 세 스 는 서로 다른 신 호 를 통 해 워 커 프로 세 스 를 제어 합 니 다. 이 방식 으로 워 커 프로 세 스 를 제어 합 니 다.이 글 은 우리 가 nginx 를 운영 하고 사용 할 때 자주 사용 하 는 신호 nginx 에 대해 어떻게 처리 하 는 지 를 다 루 고 있다.
시그 널
master 가 worker 에 게 socketpair 를 통 해 제어 신 호 를 보 내 는 것 을 소개 할 때 어떤 신호 인지, 그리고 Liux 에서 어떻게 프로 세 스에 신 호 를 보 내 는 지 미리 소개 해 야 합 니 다.신호 에 대해 서 는 소프트웨어 프로 세 스 차원 에서 하 드 인 터 럽 트 에 대한 시 뮬 레이 션 이다.소프트웨어 차원 에서 중 단 된 처 리 를 할 수 없 지만 프로 세 스 간 의 통신 과 상호작용 이 필요 할 수도 있 습 니 다. 이때 신호 가 나타 납 니 다.프로 세 스 가 신 호 를 받 은 후에 몇 가지 다른 처리 방식 이 있 습 니 다. 신 호 를 무시 할 수 있 지만 두 가지 신 호 는 무시 할 수 없습니다. SIGKILL 과 SIGSTOP;처리 함 수 를 정의 하고 신호 가 도 착 했 을 때 처리 함 수 를 호출 하여 처리 합 니 다.결 성 된 작업 을 수행 하거나Liux 에서 신호 와 관련 된 주요 조작 함 수 는 다음 과 같은 kill, raise, alarm 과 setitimer, sigquue 함수 가 있 습 니 다.각 함수 시용 장소 도 다 르 고 기능 도 다 릅 니 다. 여기 서 kill 만 소개 합 니 다. nginx 에서 사용 하 는 kill 함수 가 보 내 는 신호 이기 때 문 입 니 다.
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

man 을 참고 하면 kill 은 모든 프로 세 스 (pid) 에 모든 신호 (sig) 를 보 낼 수 있 음 을 알 수 있 습 니 다.pid 에 따라 kill 이 보 내 는 신호 범위 도 다 릅 니 다. 관심 이 있 는 사람 은 Liux 의 시스템 함 수 를 보고 구체 적 인 사용 규범 을 알 수 있 습 니 다.
nginx 신호 처리
master 에 신 호 를 보 내 는 방식 은 nginx 가 제공 하 는 명령 도 구 를 사용 하 는 것 입 니 다.
nginx -s signal

현재 nginx 는 stop, quit, reopen, reload 몇 가지 신 호 를 지원 합 니 다.모든 신호 가 대표 하 는 의 미 는 사실 단어의 의 미 를 통 해 잘 이해 할 수 있 고 여기 서 상세 하 게 설명 하지 않 는 다.마스터 가 신 호 를 받 은 후에 nginx 가 어떤 조작 을 했 는 지 설명해 야 합 니 다.다음은 reload 프로필 이라는 명령 으로 소개 합 니 다.nginx 에 신 호 를 보 내 는 전 제 는 nginx 가 이미 시작 되 어야 합 니 다. 그러면 신 호 를 받 을 때 해당 하 는 조작 을 수행 할 수 있 습 니 다. 그렇지 않 으 면 잘못 보 고 될 것 입 니 다.신호 처리 와 관련 된 논 리 를 실행 할 때 파일 을 열 어야 하기 때문에 nginx 가 시작 하지 않 을 때 이 파일 들 은 존재 하지 않 기 때문에 파일 을 여 는 오류 가 발생 할 수 있 습 니 다.그 밖 에 nginx 가 시 작 된 후에 야 어떤 신 호 를 받 았 는 지 계속 감청 할 수 있 습 니 다.작 동 하지 않 으 면 시스템 이 어떻게 처리 해 야 할 지 전혀 모른다.따라서 nginx 의 신호 처리 에 대해 이야기 하려 면 nginx 의 시작 과정 을 조금 언급 해 야 합 니 다.이 글 은 nginx 시작 에 대해 상세 하 게 소개 하지 않 고 nginx 와 관련 된 신호 처리 부분 만 설명 합 니 다.nginx 는 main 함수 에서 관련 구조 체 초기 화 작업 을 한 다음 signals 초기 화 를 진행 합 니 다.
    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }

이 방법 은 이어서 ngx 를 호출 합 니 다.process. c 중 ngxinit_signals, 구체 적 인 방법 체:
ngx_int_t ngx_init_signals(ngx_log_t *log)
{
    ngx_signal_t      *sig;
    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = sig->handler;
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1){
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "sigaction(%s) failed", sig->signame);
            return NGX_ERROR;
        }
    }
    return NGX_OK;
}

읽 기 편 하도록 비 핵심 논리 코드 를 삭제 했다.여기 서 볼 수 있 듯 이 이 방법 은 주로 모든 신호 의 처리 함 수 를 가리 키 는 데 사용 되 며, sigaction 을 통 해 모든 신호 가 올 때 어떤 방법 으로 처리 해 야 하 는 지 를 가리킨다.그 중에서 signals 는 nginx 가 정의 하 는 구조 체 의 배열 로 각 배열 의 대상 은 하나의 구조 체 이 고 이 구조 체 는 이 신호 의 명칭, 처리 방법 을 정의 하 며 구체 적 인 코드 는 다음 과 같다.
typedef struct {
    int     signo;
    char   *signame;
    char   *name;
    void  (*handler)(int signo);
} ngx_signal_t;

초기 화 신호 관련 작업 이 끝 난 후에 main 함수 가 계속 실 행 됩 니 다. 그 다음 에 master 프로 세 스 를 시작 할 지, single worker 를 시작 할 지 판단 에 따라 신호 처 리 는 master 시작 방법 에서 처 리 됩 니 다.
    if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else { ngx_master_process_cycle(cycle); }

master 프로 세 스 를 시작 하기 전에 nginx 는 설정 파일 의 해석, 핵심 구조 체 의 초기 화, 로그 의 생 성 을 포함 하여 많은 작업 을 실 행 했 습 니 다.따라서 master 시작 방법 에 서 는 프로 세 스 이름 을 설정 하고 worker 프로 세 스 를 시작 한 다음 에 for (;) 순환 에 들 어 가 는 등 마무리 작업 을 실 행 했 습 니 다.사 순환 에 서 는 신호 와 신호 의 종류 가 있 는 지 끊임없이 검사 한 후 신호 에 따라 서로 다른 동작 을 한다.nginx 가 시 작 된 후에 우 리 는 nginx - s reload 를 입력 했 을 때 nginx 는 입력 한 매개 변 수 를 분석 한 결과 신 호 를 전달 한 것 을 발견 하고 다음 에 ngx 를 실행 합 니 다.signal_process 방법, 그리고 return.이 방법 은 nginx 의 생명주기 구조 체 cycle (이 구조 체 를 어떻게 해석 해 야 할 지 모 르 겠 음) 과 신호 의 이름 을 받아들인다.이 방법 은 해당 논 리 를 판단 한 후 ngx 를 호출 합 니 다.process. c 의 방법 ngxos_signal_process。nginx 에서 모든 프로 세 스 작업 은 이 파일 에서 프로 세 스 를 생 성, 죽 이 는 방법 을 제공 합 니 다.위의 이 방법 에 대해 사실은 Liux 의 시스템 호출 kill 을 호출 한 것 입 니 다. 방법 은 다음 과 같 습 니 다.
int kill(pid_t pid, int sig)

들 어 오 는 nginx 의 pid 와 구체 적 인 신 호 를 통 해 master 에 게 해당 하 는 신호 에 따라 지정 한 작업 을 수행 하 라 고 알려 줍 니 다.핵심 논리 코드 는 다음 과 같 고 코드 가 비교적 길 며 사실은 각종 판정 이 며 서로 다른 신호 에 따라 서로 다른 처 리 를 한다.
    for ( ;; ) {
        if (delay) {
            if (ngx_sigalrm) {
                sigio = 0;
                delay *= 2;
                ngx_sigalrm = 0;
            }

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "termination cycle: %d", delay);

            itv.it_interval.tv_sec = 0;
            itv.it_interval.tv_usec = 0;
            itv.it_value.tv_sec = delay / 1000;
            itv.it_value.tv_usec = (delay % 1000 ) * 1000;

            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                              "setitimer() failed");
            }
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");

        sigsuspend(&set);

        ngx_time_update();

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "wake up, sigio %i", sigio);

        if (ngx_reap) {
            ngx_reap = 0;
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");

            live = ngx_reap_children(cycle);
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            ngx_master_process_exit(cycle);
        }

        if (ngx_terminate) {
            if (delay == 0) {
                delay = 50;
            }

            if (sigio) {
                sigio--;
                continue;
            }

            sigio = ccf->worker_processes + 2 /* cache processes */;

            if (delay > 1000) {
                ngx_signal_worker_processes(cycle, SIGKILL);
            } else {
                ngx_signal_worker_processes(cycle,
                                       ngx_signal_value(NGX_TERMINATE_SIGNAL));
            }

            continue;
        }

        if (ngx_quit) {
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

            ls = cycle->listening.elts;
            for (n = 0; n < cycle->listening.nelts; n++) {
                if (ngx_close_socket(ls[n].fd) == -1) {
                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                                  ngx_close_socket_n " %V failed",
                                  &ls[n].addr_text);
                }
            }
            cycle->listening.nelts = 0;

            continue;
        }

        if (ngx_reconfigure) {
            ngx_reconfigure = 0;

            if (ngx_new_binary) {
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_RESPAWN);
                ngx_start_cache_manager_processes(cycle, 0);
                ngx_noaccepting = 0;

                continue;
            }

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");

            cycle = ngx_init_cycle(cycle);
            if (cycle == NULL) {
                cycle = (ngx_cycle_t *) ngx_cycle;
                continue;
            }

            ngx_cycle = cycle;
            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                   ngx_core_module);
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_JUST_RESPAWN);
            ngx_start_cache_manager_processes(cycle, 1);

            /* allow new processes to start */
            ngx_msleep(100);

            live = 1;
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }

        if (ngx_restart) {
            ngx_restart = 0;
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_RESPAWN);
            ngx_start_cache_manager_processes(cycle, 0);
            live = 1;
        }

        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, ccf->user);
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_REOPEN_SIGNAL));
        }

        if (ngx_change_binary) {
            ngx_change_binary = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
            ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
        }

        if (ngx_noaccept) {
            ngx_noaccept = 0;
            ngx_noaccepting = 1;
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }
    }
}

좋은 웹페이지 즐겨찾기