nginx 의 worker 시작 분석

다 중 프로 세 스 모드 에서 워 커 작업 프로 세 스 를 시작 하 는 논 리 는 ngxstart_worker_processes 시작, 3 개의 파 라 메 터 를 포함 합 니 다. 첫 번 째 cycle 은 전역 의 핵심 구조 체 입 니 다. 두 번 째 파 라 메 터 n 은 워 커 프로 세 스 수 를 표시 합 니 다. 세 번 째 파 라 메 터 type 은 작업 프로 세 스 유형 입 니 다. 여 기 는 NGX 입 니 다.PROCESS_RESPAWN 은 워 커 프로 세 스 가 비정상적 으로 중 지 될 때 master 프로 세 스 가 새 워 커 를 다시 시작 할 것 임 을 표시 합 니 다.
#file:ngx_process_cycle.c
static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
    ngx_int_t      i;
    ngx_channel_t  ch;


    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");

    ngx_memzero(&ch, sizeof(ngx_channel_t));

    ch.command = NGX_CMD_OPEN_CHANNEL;

   //n   
    for (i = 0; i < n; i++) {
        ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type);

        ch.pid = ngx_processes[ngx_process_slot].pid;//      id
        ch.slot = ngx_process_slot; // ngx_spawn_process    
        ch.fd = ngx_processes[ngx_process_slot].channel[0];

        ngx_pass_open_channel(cycle, &ch);
    }
}

master 와 worker 프로 세 스 간 의 통신 을 토론 할 때 중요 한 데이터 구조 ngxchannel_t. 채널 이 라 고도 부 릅 니 다. 로 컬 소켓 으로 이 루어 집 니 다.아래 socketpair 방법 을 이용 하면 부자 프로 세 스 간 에 사용 할 소켓 을 만 들 수 있 습 니 다.
int socketpair(int d, int type, int protocol, int sv[2]);
socketpair 가 실 행 될 때 sv [2] 이 두 소켓 은 다음 과 같은 관계 가 있 습 니 다. sv [0 소켓 에 데 이 터 를 쓰 면 sv [1] 소켓 에서 데 이 터 를 읽 을 수 있 습 니 다. 마찬가지 로 sv [1] 소켓 에 데 이 터 를 쓸 수도 있 고 sv [0] 에서 데 이 터 를 쓸 수도 있 습 니 다.기 록 된 데 이 터 를 읽 을 수 있 습 니 다. 부모 프로 세 스 간 에 적용 할 때, 부모 프로 세 스에 서 socketpair 를 호출 하여 이 소켓 을 만 들 고, 이 어 fork 를 호출 하여 하위 프로 세 스 를 만 듭 니 다. 그리고 부모 프로 세 스에 서 sv [1] 소켓 을 닫 고, 하위 프로 세 스에 서 sv [0] 소켓 을 닫 습 니 다. 부모 프로 세 스 는 sv [0] 와 하위 프로 세 스 의 sv [1] 를 사용 할 수 있 습 니 다.양 방향 통신 을 자 유 롭 게 진행 합 니 다. for loop 에 서 는 ngx spawn process 를 호출 합 니 다. 이 함 수 는 주로 socketpair 를 호출 하여 스 트림 소켓 그룹 을 만 든 다음 fork 를 호출 하여 하위 프로 세 스 를 만 들 고 하위 프로 세 스 에서 프로 세 스 함수 ngx worker process cycle 을 실행 합 니 다. 새로운 worker 를 만 들 때마다 다른 worker 에 새 worker 정 보 를 동기 화 해 야 합 니 다. 이것 은 ngx pass open channel 입 니 다.완성, 두 번 째 매개 변 수 는 ngx channel t 를 가리 키 는 지침 입 니 다. ngx channel t 구 조 는 다음 과 같 습 니 다.
typedef struct {
ngx uint t command; / 전 달 된 TCP 메시지 의 명령
ngx pid t pid; / 프로 세 스 id
ngx int t; / ngx processes 프로 세 스 배열 의 번호
ngx fd t fd; / 통신 의 소켓 핸들
}ngx_channel_t;
이 간단 한 구 조 는 master 프로 세 스 와 worker 프로 세 스 간 의 상 태 를 동기 화 하 는 데 사 용 됩 니 다. 새로운 프로 세 스 를 만 들 때마다 채널 의 pid 구성원 은 새 프로 세 스 의 id 로 할당 합 니 다. slot 는 새 프로 세 스 가 ngx processes 프로 세 스 배열 의 번호 로 할당 합 니 다. fd 는 socketpair 가 만 든 프로 세 스 그룹 중의 sv [0] 입 니 다.이 프로 세 스 배열 의 정 보 는 ngx process t 구조 체 의 channel 구성원 에 저 장 됩 니 다. ngx process t 는 프로 세 스 를 설명 하 는 구조 입 니 다.
static void
ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
{
    ngx_int_t  i;

    for (i = 0; i < ngx_last_process; i++) {

        if (i == ngx_process_slot
            || ngx_processes[i].pid == -1
            || ngx_processes[i].channel[0] == -1)
        {
            continue;
        }

        ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                      "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
                      ch->slot, ch->pid, ch->fd,
                      i, ngx_processes[i].pid,
                      ngx_processes[i].channel[0]);

        ngx_write_channel(ngx_processes[i].channel[0],
                          ch, sizeof(ngx_channel_t), cycle->log);
    }
}

ngx pass open channel 함수 에서 for 순환 은 매번 새로 만 들 지 않 은 유효한 worker 프로 세 스 의 색인 을 가 져 오고 ngx write channel 함 수 를 통 해 이 worker 에 게 새 프로 세 스 정 보 를 전달 합 니 다. 그 중에서 ngx write channel 의 첫 번 째 매개 변 수 는 이 worker 와 통신 하 는 스 트림 소켓 배열 의 channel [0] 입 니 다. worker 프로 세 스 는 channel 1] 에서 전 달 된 정 보 를 받 습 니 다.워 커 프로 세 스 가 채널 [1] 에 메시지 가 도착 했다 는 것 을 어떻게 알 았 는 지 의문 입 니 다. 워 커 프로 세 스 함수 에서 채널 [1] 의 읽 기 이 벤트 를 이벤트 구동 에 추가 하고 읽 기 이벤트 리 셋 함 수 를 ngx 로 연결 하기 때 문 입 니 다.channel_handler。
static void
ngx_channel_handler(ngx_event_t *ev)
{
    ngx_int_t          n;
    ngx_channel_t      ch;
    ngx_connection_t  *c;

    if (ev->timedout) {
        ev->timedout = 0;
        return;
    }

    c = ev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler");

    for ( ;; ) {

        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);	// fd    

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);

        if (n == NGX_ERROR) {

            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
                ngx_del_conn(c, 0);
            }

            ngx_close_connection(c);
            return;
        }

        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return;
            }
        }

        if (n == NGX_AGAIN) {
            return;	//        
        }

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
                       "channel command: %d", ch.command);

        switch (ch.command) {

        case NGX_CMD_QUIT:
            ngx_quit = 1;
            break;

        case NGX_CMD_TERMINATE:
            ngx_terminate = 1;
            break;

        case NGX_CMD_REOPEN:
            ngx_reopen = 1;
            break;

        case NGX_CMD_OPEN_CHANNEL:

            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
                           "get channel s:%i pid:%P fd:%d",
                           ch.slot, ch.pid, ch.fd);

            ngx_processes[ch.slot].pid = ch.pid;
            ngx_processes[ch.slot].channel[0] = ch.fd;
            break;

        case NGX_CMD_CLOSE_CHANNEL:

            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
                           "close channel s:%i pid:%P our:%P fd:%d",
                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,
                           ngx_processes[ch.slot].channel[0]);

            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                              "close() channel failed");
            }

            ngx_processes[ch.slot].channel[0] = -1;
            break;
        }
    }
}

좋은 웹페이지 즐겨찾기