Nginx 학습 의 7 - 모듈 ngxepoll_module 상세 설명 (epoll 메커니즘 이 nginx 에서 의 실현)
모듈 정의 (src / event / ngx epoll module. c):
ngx_module_t ngx_epoll_module = {
NGX_MODULE_V1,
&ngx_epoll_module_ctx, /* module context */
ngx_epoll_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
우선 ngxepoll_module 모듈 은 어떤 설정 항목 에 관심 이 있 습 니까? ngxepoll_commands 배열 정 의 는 다음 과 같 습 니 다.
static ngx_command_t ngx_epoll_commands[] = {
// epoll_wait
{ ngx_string("epoll_events"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,//
0,
offsetof(ngx_epoll_conf_t, events),
NULL },
// I/O io_setup I/O , I/O
{ ngx_string("worker_aio_requests"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_epoll_conf_t, aio_requests),
NULL },
ngx_null_command
};
설정 항목 을 저장 하 는 구조 체 ngxepoll_conf_t:
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
NULL, /* process the changes */
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
유형 ngxevent_module_t(src/event/ngx_event.h):
typedef struct {
ngx_str_t *name;//
void *(*create_conf)(ngx_cycle_t *cycle);// ,
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);// ,
ngx_event_actions_t actions;
} ngx_event_module_t;
그 중에서 actions 는 사건 구동 체제 에 대응 하여 모든 사건 모듈 이 실현 해 야 할 10 가지 추상 적 인 방법 이다.
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);//
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);//
// ,
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);// ,
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
// , Nginx
ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
// , 。 ngx_process_events_and_timers , 、
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);//
void (*done)(ngx_cycle_t *cycle);//
} ngx_event_actions_t;
다음은 몇 가지 중요 한 방법 을 보 겠 습 니 다.
init 인 터 페 이 스 를 실현 하 는 방법 ngxepoll_init, 그것 은 두 가지 일 을 했다.
(1) epoll 호출create 방법 epoll 대상 만 들 기
(2) 이벤트 생 성list 배열, epoll 진행 에 사용wait 호출 시 커 널 대상 전달
원본 코드:
static int ep = -1;//epoll
static struct epoll_event *event_list;//
static ngx_uint_t nevents;//
static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_epoll_conf_t *epcf;//
epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);//
if (ep == -1) {
// epoll , Linux ,
ep = epoll_create(cycle->connection_n / 2);
//
if (ep == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"epoll_create() failed");
return NGX_ERROR;
}
#if (NGX_HAVE_FILE_AIO)
// I/O
ngx_epoll_aio_init(cycle, epcf);
#endif
}
// epoll_wait
if (nevents < epcf->events) {
if (event_list) {
//
ngx_free(event_list);
}
//
event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
//
nevents = epcf->events;
ngx_io = ngx_os_io;
//Nginx
ngx_event_actions = ngx_epoll_module_ctx.actions;
#if (NGX_HAVE_CLEAR_EVENT)
// E epoll,NGX_HAVE_CLEAR_EVENT nginx ET
ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
|NGX_USE_GREEDY_EVENT
|NGX_USE_EPOLL_EVENT;
return NGX_OK;
}
다음은 ngxepoll_add_이벤트 방법 을 예 로 들 면 그들 이 epoll 를 어떻게 호출 하 는 지 봅 시다.ctl 에서 epoll 에 이 벤트 를 추가 합 니 다.
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee;
// data ngx_connection_t
c = ev->data;
// event , events EPOLLIN EPOLLOUT
events = (uint32_t) event;
// : active
/* ,
man epoll:
Q: What happens if you register the same file descriptor on an epoll instance twice?
A: You will probably get EEXIST. However, it is possible to add a duplicate (dup(2), dup2(2), fcntl(2) F_DUPFD) descriptor to the same epoll instance. This can be a useful technique for filtering events, if the duplicate file descriptors are registered with different events masks.*/
/* nginx , epoll fd ( NGX_READ_EVENT) ,nginx fd , e=c->write, e->active 1, fd NGX_WRITE_EVENT epoll , mod , , add , fd epoll 。 NGX_WRITE_EVENT 。*/
if (event == NGX_READ_EVENT) {
e = c->write;
prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN)
events = EPOLLIN;
#endif
} else {
e = c->read;
prev = EPOLLIN;
#if (NGX_WRITE_EVENT != EPOLLOUT)
events = EPOLLOUT;
#endif
}
// active
if (e->active) {
op = EPOLL_CTL_MOD;// ,
events |= prev;
} else {
op = EPOLL_CTL_ADD;// ,
}
ee.events = events | (uint32_t) flags;// events
//ptr ngx_connection_t ,instance
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"epoll add event: fd:%d op:%d ev:%08XD",
c->fd, op, ee.events);
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
ev->active = 1;
#if 0
ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
#endif
return NGX_OK;
}
ngx_epoll_process_이 벤트 는 이 벤트 를 수집, 배포 하 는 프로 세 스 를 실현 합 니 다.이벤트 인터페이스의 방법, 코드 는 다음 과 같 습 니 다.
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int events;
uint32_t revents;
ngx_int_t instance, i;
ngx_uint_t level;
ngx_err_t err;
ngx_event_t *rev, *wev, **queue;
ngx_connection_t *c;
/* NGX_TIMER_INFINITE == INFTIM */
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll timer: %M", timer);
// epoll_wait
events = epoll_wait(ep, event_list, (int) nevents, timer);
err = (events == -1) ? ngx_errno : 0;
//
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
//epoll_wait
if (err) {
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
return NGX_ERROR;
}
//
if (events == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"epoll_wait() returned no events without timeout");
return NGX_ERROR;
}
ngx_mutex_lock(ngx_posted_events_mutex);
// epoll_wait
for (i = 0; i < events; i++) {
// ngx_connection_t
c = event_list[i].data.ptr;
// : instance ,
instance = (uintptr_t) c & 1;
// 32 64 , 0,
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
//
rev = c->read;
//
if (c->fd == -1 || rev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
//
revents = event_list[i].events;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: fd:%d ev:%04XD d:%p",
c->fd, revents, event_list[i].data.ptr);
if (revents & (EPOLLERR|EPOLLHUP)) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll_wait() error on fd:%d ev:%04XD",
c->fd, revents);
}
#if 0
if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange epoll_wait() events fd:%d ev:%04XD",
c->fd, revents);
}
#endif
if ((revents & (EPOLLERR|EPOLLHUP))
&& (revents & (EPOLLIN|EPOLLOUT)) == 0)
{
/*
* if the error events were returned without EPOLLIN or EPOLLOUT,
* then add these flags to handle the events at least in one
* active handler
*/
revents |= EPOLLIN|EPOLLOUT;
}
//
if ((revents & EPOLLIN) && rev->active) {
if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
rev->posted_ready = 1;
} else {
rev->ready = 1;
}
//
if (flags & NGX_POST_EVENTS) {
/* post ,
ngx_posted_accept_events ngx_posted_events 。*/
queue = (ngx_event_t **) (rev->accept ?
&ngx_posted_accept_events : &ngx_posted_events);
//
ngx_locked_post_event(rev, queue);
} else {
//
rev->handler(rev);
}
}
wev = c->write;
if ((revents & EPOLLOUT) && wev->active) {
if (c->fd == -1 || wev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
if (flags & NGX_POST_THREAD_EVENTS) {
wev->posted_ready = 1;
} else {
wev->ready = 1;
}
if (flags & NGX_POST_EVENTS) {
ngx_locked_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
}
ngx_mutex_unlock(ngx_posted_events_mutex);
return NGX_OK;
}
위 코드 에 나타 난 만 료 사건 을 설명 하 겠 습 니 다.
예 를 들 어 epollwait 는 한 번 에 세 개의 사건 을 되 돌려 줍 니 다. 첫 번 째 사건 을 처리 하 는 과정 에서 업무 의 필요 로 연결 을 닫 았 습 니 다. 이 연결 은 세 번 째 사건 에 적합 합 니 다.이렇게 해서 세 번 째 사건 을 처리 할 때 이 사건 은 기한 이 지난 사건 이 고 처리 하면 실 수 를 할 수 있다.
그렇다면 이 문 제 는 어떻게 해결 할 것 인가?닫 힌 이 연결 의 fd 소켓 을 - 1 로 설정 합 니까?이렇게 하면 결코 모든 문 제 를 해결 할 수 없다.원인 은 ngxconnection_t 의 복용.
세 번 째 사건 에 대응 하 는 ngx 를 가정 합 니 다.connection_t 연결 중의 fd 소켓 은 원래 10 이 었 습 니 다. 첫 번 째 사건 을 처리 할 때 이 연결 의 소켓 을 닫 았 습 니 다. 동시에 - 1 로 설정 하고 ngx 를 호출 합 니 다.free_connection 은 이 연결 을 연결 풀 에 돌려 줍 니 다.재 ngxprocess_이벤트 방법의 순환 에서 i 가 두 번 째 사건 을 처리 하기 시 작 했 습 니 다. 마침 두 번 째 사건 은 새로운 연결 사건 을 만 들 고 ngx 를 호출 하 는 것 입 니 다.get_connection 이 연결 풀 에서 꺼 낸 연결 은 방금 풀 린 세 번 째 이벤트 에 대응 하 는 연결 일 수 있 습 니 다.소켓 10 이 방금 풀 려 났 기 때문에 Liux 커 널 은 방금 풀 린 소켓 10 을 새로 만 든 연결 에 배분 할 가능성 이 매우 높다.따라서 세 번 째 사건 을 순환 적 으로 처리 할 때 이 시간 은 만 료 된다!새로 만 든 연결 이 아니 라 닫 힌 연결 입 니 다.
어떻게 이 문 제 를 해결 합 니까?인 스 턴 스 로 위 치 를 표시 합 니 다.호출 시 ngxget_connection 이 연결 풀 에서 새로운 연결 을 가 져 올 때 인 스 턴 스 플래그 위치 가 거꾸로 설 치 됩 니 다.다음은 함수 ngxget_connection 에 대응 하 는 코드 (src / core / ngx connection. c):
ngx_connection_t *
ngx_get_connection(ngx_socket_t s, ngx_log_t *log)
{
ngx_uint_t instance;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
/* disable warning: Win32 SOCKET is u_int while UNIX socket is int */
if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"the new socket has number %d, "
"but only %ui files are available",
s, ngx_cycle->files_n);
return NULL;
}
/* ngx_mutex_lock */
//
c = ngx_cycle->free_connections;
if (c == NULL) {
ngx_drain_connections();
c = ngx_cycle->free_connections;
}
if (c == NULL) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"%ui worker_connections are not enough",
ngx_cycle->connection_n);
/* ngx_mutex_unlock */
return NULL;
}
ngx_cycle->free_connections = c->data;
ngx_cycle->free_connection_n--;
/* ngx_mutex_unlock */
if (ngx_cycle->files) {
ngx_cycle->files[s] = c;
}
//
rev = c->read;
wev = c->write;
ngx_memzero(c, sizeof(ngx_connection_t));
c->read = rev;
c->write = wev;
c->fd = s;
c->log = log;
// instance
instance = rev->instance;
ngx_memzero(rev, sizeof(ngx_event_t));
ngx_memzero(wev, sizeof(ngx_event_t));
//instance
rev->instance = !instance;
wev->instance = !instance;
rev->index = NGX_INVALID_INDEX;
wev->index = NGX_INVALID_INDEX;
rev->data = c;
wev->data = c;
wev->write = 1;
return c;
}
이렇게 하면 어떠한 비용 도 증가 하지 않 고 서버 개발 시 발생 하 는 기한 이 지난 사건 문 제 를 해결 할 수 있다.
done 인 터 페 이 스 를 실현 하 는 ngxepoll_done 방법 은 nginx 가 서 비 스 를 종료 할 때 호출 됩 니 다.주로 완 료 된 작업 은 epoll 설명자 ep 를 닫 고 이벤트 를 방출 하 는 것 입 니 다.list 배열.
참고 자료:
CSDN 블 로그: ngxepoll_add_이벤트 설명
& lt; Nginx 깊이 이해 & gt;
http://www.alidata.org/archives/1296
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
용감한 바로 가기 및 우분투 응용 프로그램안녕하세요 여러분, 이 기사에서는 모든 사이트에서 pwa를 생성하고 실행기 응용 프로그램으로 추가하는 방법을 설명하고 싶습니다. 일부 웹사이트는 PWA로 설치를 허용하지 않지만 유사한 애플리케이션을 원합니다. 1. ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.