epoll() 함수는 select()와 poll() 함수와 동일하게 I/O 멀티플렉싱으로 사용된다.
단 epoll() 함수는 리눅스에서만 호환성을 갖는 한계가 있다.
epoll() 함수는 기존 두 함수에 비해서 파일 디스크립터의 준비 상태를 알리는 방식에서
더 유연성을 갖는다.
1.1. 상태 변화를 알리는 방식
Level-Triggered Notification
- 상태 변화를 감지하고자 하는 파일 디스크립터가 준비되면 무조건 알리는 방식
Edge-Triggered Notification
- 항상 새로 준비된 파일 디스크립터에 대해서만 알리는 방식
두 알림 방식이 마음에 잘 왔다지 않을 것이다. 쉽게 구분하는 방법은 두 번째 방식은 일단 A라는
파일 디스크립터에 대해서 알림을 받았는데 다른 새로운 파일 디스크립터가 준비되기 전에
다시 같은 A가 알림을 받을 준비가 되면 생략한다. 하지만 첫번째는 중복과 상관없이 무조건 알린다.
1.2. 각 함수별로 알림 지원 여부
I/O 모델
Level-Triggered
Edge-Triggered
select(), poll()
O
X
시그널 기반 I/O
X
O
epoll()
O
O
시그널 기반 I/O에 대한 설명은 생략한다. 개인적으로 그 다지 좋은 방법은 아닌 것 같아서 ...
2. epoll() API 함수들
2.1. epoll_create() 함수
#include <sys/epoll.h>
int epoll_create(int size);
성공하면 파일 디스크립터를 리턴하고, 에러가 발생하면 '-1'을 리턴
매개 변수 size에는 감시할 파일 디스크립터의 수를 명시한다.
성공하면 epoll_create()는 자신의 다른 함수에서 사용할 새로운 파일 디스크립터를 리턴한다.
물론 dup() 함수 같은 것으로 다른 파일 디스크립터처럼 복사하고 이용할 수 있다.
2.2. epoll_ctl() 함수 : 관심 목록 변경 및 알림 방식 변경
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴
[주의 사항] 디스크 파일 디스크립터는 대상에서 제외된다.
op 매개 변수
op
설명
EPOLL_CTL_ADD
관심있는 파일디스크립터를 추가
EPOLL_CTL_MOD
기존 파일 디스크립터를 수정
EPOLL_CTL_DEL
기존 파일 디스크립터를 관심 목록에서 삭제
epoll_event 구조체
struct epoll_event{
uint32_t events; /* epoll 이벤트 (비트 마스트) */
epoll_data_t data; /* 사용자 데이터 */
};
typedef union epoll_data {
void *ptr; /* 사용자 정의 데이터 포인터 */
int fd; /* 파일 디스크립터 */
uint32_t u32; /* 32비트 정수 */
uint64_t u64; /* 64비트 정수 */
} epoll_data_t;
2.3. epoll_wait() : 알림 기다림.
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);
성공하면 준비된 파일 디스크립터 수 리턴, '0'은 타임아웃 경우, '-1'은 에러를 말한다.
준비된 파일 디스크립터를 배열로 리턴한다. maxevents는 리턴한 배열의 갯수이다.
timeout 값의 의미는 앞의 select(), poll()과 동일하므로 생략한다.
epoll events 필드 가능한 비트 마스크
비트
입력
리턴
설명
EPOLLIN
O
O
높은 순위 데이터를 제외한 데이터를 읽을 수 있다.
EPOLLPRI
O
O
높은 순위 데이터를 읽을 수 있다.
EPOLLRDHUP
O
O
상대편 소켓 shutdown 확인
EPOLLOUT
O
O
일반 데이터를 기록할 수 있다.
EPOLLET
O
X
에지 트리거를 알림 방식으로 선택 (기본은 레벨트리거 임)
EPOLLONESHOT
O
X
이벤트를 알린 후에 이벤트 감시를 비활성화
EPOLLERR
X
O
에러가 발생함.
EPOLLHUP
X
O
행업 발생
많은 면에서 poll() 함수의 이벤트와 유사하다.
3. 예제
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/epoll.h>
#define MAX_BUFFER_LEN 256#define MAX_EVENT_NUM 1int main(int argc, char *argv[])
{
int retval, m_read, m_write;
int i;
int fd;
int epfd;
struct epoll_event ev;
struct epoll_event evlist[MAX_EVENT_NUM];
char buffer[MAX_BUFFER_LEN];
if((fd = open("./temp.txt", O_RDWR)) == -1)
{
perror("open : ");
exit(EXIT_FAILURE);
}
/* epoll create */
epfd = epoll_create(1);
if(epdf == -1)
{
perror("epoll_create : ");
exit(EXIT_FAILURE);
}
/* epoll control */
ev.events = EPOLLIN;
ev.data.fd = STDIN_FILENO;
if(epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
{
perror("epoll_ctl : ");
exit(EXIT_FAILURE);
}
while(1)
{
memset(buffer, 0x00, MAX_BUFFER_LEN);
retval = epoll_wait(epfd, evlist, MAX_EVENT_NUM, -1);
if(retval == -1)
{
/* signal interrupt */if(errno == EINTR)
{
continue;
}
else
{
perror("epoll_wait : ");
exit(EXIT_FAILURE);
}
}
printf("retval = %d \n", retval);
for(i = 0; i < MAX_EVENT_NUM; i++)
{
if(evlist[i].events & EPOLLIN)
{
m_read = read(STDIN_FILENO, buffer, MAX_BUFFER_LEN);
printf("[read] m_read = %d \n", m_read);
m_write = write(fd, buffer, m_read);
printf("[write] m_write = %d \n", m_write);
}
}
usleep(1000);
}
close(fd);
exit(EXIT_SUCCESS);
}
select()와 비슷한 작업을 수행한다. select() 함수가 BSD로부터 기원한다면 poll() 함수는 시스템 V로부터
기원한다. 두 함수간에 차이점으로는 파일디스크립터를 관리하는 방식면에서 서로 다르다.
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
준비된 파일 디스크립터 수를 리턴한다.
타임 아웃일 경우 '0'을, 에러가 발생하면 '-1'을 리턴한다.
pollfd 구조체는 아래와 같다. nfds는 pollfd 배열의 갯수를 의미한다.
struct pollfd {
int fd; /* 파일 디스크립터 */
short events; /* 확인하기를 원하는 이벤트에 대한 비트마스크 */
short revents; /* 리턴된 이벤트 마스크 */
};
1.1. timeout 값
값
설명
-1
pollfd 구조체에 포함된 파일 디스크립터 중 하나가 준비되거나 이벤트를 수신할 때까지
블록한다. (블록의 정도는 파일디스크립터의 종류에 따라 다르다)
0
블록하지 않고 파일디스크립터가 준비 상태인지만을 확인한다.
0 보다 큰값
최대 설정된 타임아웃 기간만큼 블록한다.
1.2. 리턴값
값
설명
-1
에러가 발생
0
파일 디스크립터가 준비되기 전에 타임아웃된 경우
양수
준비된 파일 디스크립터의 갯수
2. events/revents에 대한 비트 마스크 값
비트
events
revents
설명
POLLIN
O
O
높은 우선순위 데이터 외의 데이터를 읽을 수 있다.
POLLRDNORM
O
O
상동 (리눅스는 POLLIN과 동일하게 동작)
POLLRDBAND
O
O
우선순위 데이터를 읽을 수 있다. (리눅스 사용 안함)
POLLPRI
O
O
높은 우선순위 데이터를 읽을 수 있다.
POLLRDHUP
O
O
상대편 소켓 셧다운
POLLOUT
O
O
일반 데이터를 기록할 수 있다.
POLLWRNORM
O
O
상동 (리눅스는 POLLOUT과 동일하게 동작)
POLLWRBAND
O
O
우선순위 데이터를 기록할 수 있다.
POLLERR
X
O
에러가 발생했다.
POLLHUP
X
O
장애가 발생했다.
POLLNVAL
X
O
파일 디스크립터가 열리지 않는다.
POLLMSG
X
X
리눅스에서는 사용하지 않음.
3. 파일 디스크립터의 준비 상태
select()와 poll() 함수는 모든 파일 디스크립터 종류에서 블록되지는 않는다. 파일 디스크립터의 종류마다
블록의 동작이 다르다. 예로 일반 파일에 대한 파일 디스크립터는 블록하지 않고 바로 리턴한다.
이 예제가 select() 함수편의 예제에서 select() 함수의 리턴값을 찍어보면 알 수 있다.
따라서 I/O 멀티플렉싱 기법을 사용 시에는 파일 디스크립터의 종류에 대한 블록 동작에 대해서 잘 살펴봐야 한다.
4. 예제
select() 함수 예제 편에서 디스크 파일 디스크립터를 대상으로 했다면, 여기서는 표준 입력을 블록 대상으로 하였다.
select() 함수 편에서 retval값을 보면 실제 블록되지 않는다. 항상 준비된 상태가 된다.
파일 디스크립터 종류에 따른 블록되는 현상 차이를 보기위해서 poll() 예제에서는 표준 입력을 대상으로 예제를
작성하였다. 두 예제 간의 차이점을 보는 것도 도움이 될 것이다.
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <poll.h>
#define MAX_BUFFER_LEN 256int main(int argc, char *argv[])
{
int retval, m_read, m_write;
int fd;
struct pollfd pollfds[1];
char buffer[MAX_BUFFER_LEN];
if((fd = open("./temp.txt", O_RDWR)) == -1)
{
perror("open : ");
exit(EXIT_FAILURE);
}
pollfds[0].fd = STDIN_FILENO;
pollfds[0].events = POLLIN;
while(1)
{
memset(buffer, 0x00, MAX_BUFFER_LEN);
retval = poll(pollfds, 1, -1);
if(retval == -1)
{
perror("poll : ");
exit(EXIT_FAILURE);
}
printf("retval = %d \n", retval);
if(pollfds[0].revents & POLLIN)
{
m_read = read(STDIN_FILENO, buffer, MAX_BUFFER_LEN);
printf("[read] m_read = %d \n", m_read);
m_write = write(fd, buffer, m_read);
printf("[write] m_write = %d \n", m_write);
}
usleep(1000);
}
close(fd);
exit(EXIT_SUCCESS);
}