Epoll
epoll은 select의 단점을 보완하여 사용할 수 있도록 만든 I/O 통지 모델이다. 파일 디스크립터를 사용자가 아닌 커널이 관리하며 그만큼 cpu 는 계속해서 파일 디스크립터의 상태 변화를 감시할 필요가 있다. 즉 select 처럼 어느 파일 디스크립터에 이벤트가 발생하였는지 찾기 위해 전체 파일디스크립터에 대해서 순차 검색을 위한 FD_ISSET 루프를 돌려야 하지만 Epoll의 경우 이벤트가 발생한 파일 디스크립터들만 구조체 배열을 통해 넘겨주므로 메모리 카피에 대한 비용이 줄어든다.
Epoll의 주요기능
- non block I/O 지: epoll은 non block 모드로 파일 디스크립터를 처리할 수 있다.
- 높은 성능: 파일 디스크립터 집합을 다룰 때 매우 효율적이다.
- Edge Trigger(ET), Level Trigger(LT): epoll은 두가지 모드로 동작할 수 있다.
Epoll 주요함수
- epoll_create1: epoll 인스턴스를 생성한다.
- epoll_ctl: 파일 디스크립터를 epoll 인스턴스에 추가, 수정, 삭제한다.
- epoll_wait: 이벤트가 발생할 때까지 대기한다.
struct epoll_event: epoll_event 구조체는 감시할 파일 디스크립터의 이벤트 유형과 사용자 데이터를 포함한다.
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
- events: 감지할 이벤트의 비트 마스크. 여러 이벤트를 OR 연산자로 결합할 수 있습니다.
- EPOLLIN: 수신할 데이터가 있습니다.
- EPOLLOUT: 송신할 준비가 되었습니다.
- EPOLLPRI: 중요 데이터(OOB)가 수신되었습니다.
- EPOLLRDHUP: 연결 종료 또는 반닫기(Half-close)가 발생했습니다.
- EPOLLERR: 에러가 발생했습니다.
- EPOLLET: 엣지 트리거 방식으로 설정합니다. 기본은 레벨 트리거 방식입니다.
- EPOLLONESHOT: 한 번만 이벤트를 받습니다. 이벤트가 발생한 후에는 다시 설정해야 합니다.
- data: 사용자 데이터. 이벤트가 발생한 파일 디스크립터 또는 사용자 정의 포인터를 포함할 수 있습니다.
Epoll 함수 Usage
#include <sys/epoll.h>
int epoll_create1(int flags);
- flags: epoll 인스턴스를 생성할 때 사용할 추가 옵션을 지정합니다. 예를 들어, EPOLL_CLOEXEC 플래그를 사용할 수 있습니다. 만약 특별한 플래그를 지정하지 않으려면 0을 전달하면 됩니다.
- EPOLL_CLOEXEC: 이 플래그를 설정하면 exec 계열의 함수 호출 시 생성된 epoll 파일 디스크립터가 자동으로 닫힙니다. 이는 자식 프로세스가 불필요하게 파일 디스크립터를 상속받는 것을 방지하기 위해 사용됩니다.
- 성공 시: 새로 생성된 epoll 인스턴스의 파일 디스크립터를 반환합니다.
- 실패 시: -1을 반환하며, errno를 통해 에러를 설명할 수 있습니다.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- epfd: epoll 인스턴스의 파일 디스크립터.
- op: 수행할 작업. EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL 중 하나.
- EPOLL_CTL_ADD: fd를 epfd의 관심 목록에 추가합니다. 이미 목록에 존재한다면 EEXIST 에러를 발생시킵니다.
- EPOLL_CTL_MOD: event에 지정된 정보를 이용해 fd의 설정을 변경합니다. 관심 목록에 없는 fd라면 ENOENT 에러를 발생시킵니다.
- EPOLL_CTL_DEL: epfd에서 fd를 제거합니다. 관심 목록에 없는 fd를 제거하려면 ENOENT 에러를 발생시킵니다. fd를 닫으면 epoll 관심 목록에서 자동으로 제거됩니다.
- fd: epoll 인스턴스에 추가, 수정 또는 삭제할 파일 디스크립터.
- event: epoll 이벤트를 설명하는 구조체. 파일 디스크립터의 관심 이벤트와 사용자 데이터를 포함.
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- epfd: epoll 인스턴스의 파일 디스크립터입니다.
- events: 이벤트가 발생한 파일 디스크립터의 목록을 저장할 배열입니다.
- epoll_event 구조체 배열입니다. 이벤트가 발생한 파일 디스크립터들이 여기에 저장됩니다.
- 각 배열 요소는 struct epoll_event 타입입니다.
- 예를 들어, struct epoll_event events[MAX_EVENTS];와 같이 정의할 수 있습니다.
- maxevents: 처리할 수 있는 최대 이벤트 수를 지정합니다.
- 한
- 번에 처리할 최대 이벤트 수를 지정합니다.
- 이 값을 통해 이벤트 배열의 크기를 제한할 수 있습니다.
- 예를 들어, maxevents를 10으로 설정하면 한 번에 최대 10개의 이벤트만 처리합니다.
- timeout: 대기 시간(밀리초)입니다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define MAX_EVENTS 10
int make_non_blocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl");
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl");
return -1;
}
return 0;
}
파일 디스크립터를 non blocking 모드로 설정한다.
int main() {
int epoll_fd, nfds;
struct epoll_event event, events[MAX_EVENTS];
// epoll 인스턴스 생성
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
// 표준 입력을 비차단 모드로 설정
if (make_non_blocking(STDIN_FILENO) == -1) {
close(epoll_fd);
exit(EXIT_FAILURE);
}
// 이벤트 설정
event.events = EPOLLIN | EPOLLET;
event.data.fd = STDIN_FILENO;
epoll_create1에서 epoll 인스턴스를 생성한다.
// 파일 디스크립터를 epoll 인스턴스에 추가
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event) == -1) {
perror("epoll_ctl");
close(epoll_fd);
exit(EXIT_FAILURE);
}
epoll_ctl에서 표준입력(stdin)을 epoll 인스턴스에 추가하고 EPOLLIN 및 EPOLLET 이벤트를 설정한다.
// 이벤트 감시 루프
while (1) {
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
if (errno == EINTR) {
continue; // 인터럽트된 경우 다시 대기
}
perror("epoll_wait");
close(epoll_fd);
exit(EXIT_FAILURE);
}
// 감지된 이벤트 처리
for (int n = 0; n < nfds; ++n) {
if (events[n].events & EPOLLIN) {
printf("Read event on file descriptor %d\n", events[n].data.fd);
char buf[512];
ssize_t count;
while ((count = read(events[n].data.fd, buf, sizeof(buf))) > 0) {
printf("Read %zd bytes: %.*s", count, (int)count, buf);
}
if (count == -1 && errno != EAGAIN) {
perror("read");
close(epoll_fd);
exit(EXIT_FAILURE);
}
}
}
}
close(epoll_fd);
return 0;
}
epoll_wait에서 이벤트가 발생할 때까지 대기한다.
'네트워크 보안 > 네트워크' 카테고리의 다른 글
strongswan #1 IKEv2 소프트웨어 아키텍쳐, 메세지 포맷, config 설정 및 실행 방법 (0) | 2024.11.03 |
---|---|
TCP/IP Stack 개발 #1 Ethernet & ARP (0) | 2024.08.04 |
Scalable Network Programming (3) | 2024.03.18 |
[Python/ MQTT] MQTT – Pub/Sub 모델 구현 (2) | 2022.08.26 |
[Django] Jump to Django (02/admin) (0) | 2022.06.30 |