Epoll
epoll이란?
Epoll은 리눅스에서 select의 단점을 보완하여 사용할 수 있도록 만든 I/O통지 모델이다.
select() 를 사용할 때에는, 유저(=프로그래머)가 관찰할 파일 디스크립터 배열을 가지고 있고, select 함수가 호출될 때마다 전체 파일 디스크립터가 해당 배열로 복사된다. 하지만 epoll에서는 커널 공간이 파일 디스크립터를 관리하고 변경된 파일 디스크립터만을 유저에게 통지해 주기 때문에 select보다 빠르게 동작할 수 있다.
* epoll은 리눅스에서 제공하는 시스템 콜이기 때문에 다른 운영체제로의 이식성이 떨어지는 게 단점이다.
epoll 관련 함수
epoll_create
#incude <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
커널 공간에 epoll 파일 디스크립터를 생성하는 함수. 리눅스 2.6.8부터는 인자인 size를 무시하기 때문에 0 이상의 값으로만 넣어주면 된다. epoll_create1은 size대신 flag를 받는다.
- flag의 종류
- EPOLL_CLOEXEC ****: exec으로 자식 프로세스가 새로운 작업에 도입할 때 파일 디스크립터를 닫아준다.
반환 값으로는 epoll의 파일 디스크립터가 전달된다.
epoll_ctl
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll에 관찰 대상인 파일 디스크립터를 등록하는 함수. 첫 번째 인자부터 순서대로 epoll fd, 옵션, 대상 파일 디스크립터, 이벤트 구조체이다.
- 옵션
epoll_ctl로 탐지할 파일 디스크립터를 등록하거나, 수정하거나, 삭제할 수 있다.
- EPOLL_CTL_ADD
- EPOLL_CTL_MOD
- EPOLL_CTL_DEL
- 이벤트 구조체
이벤트 구조체는 다음으로 이루어져 있다.
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 */
};
epoll_data 구조체의 ptr은 이벤트가 발생한 대상 객체를 가리킨다. 나머지는 잘 안쓰이는듯...? epoll_event 구조체의 events는 발생할 수 있는 epoll_event를 가리키는 값으로, 각각 한 비트로 세팅되어 있다.
정상적으로 등록되면 0이, 에러가 발생하면 -1이 반환되며, errno에 에러 정보가 세팅된다.
epoll_wait
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epoll_ctl로 등록한 파일 디스크립터의 변화를 탐지한다.
이벤트가 발생할 경우 두번째 인자로 전달된 epoll_events 포인터에 차곡차곡 쌓이게 되며, amxevents 갯수만큼만 쌓을 수 있다. timeout은 blocking할 시간을 정하는 인자이다. -1은 이벤트가 도착할 때까지 블록되며, 0일 경우 바로 반환한다. 그 이상일 경우 주어진 시간(단위는 ms)만큼 블록하고 이후 반환한다.
유저는 epoll_wait를 통해 들어온 이벤트에 대한 처리 방법을 구현하여 활용할 수 있다.
level vs edge trigger
epoll의 이벤트 탐지 방법은 level-trigger와 edge-trigger 두 가지가 있다. level-trigger는 특정한 조건이 유지되는 동안 계속 감지를 하는 반면, edge-trigger는 특정 조건이 변할 때에 감지한다. level-trigger의 조건을 1로 설정했다면, epoll은 fd가 1을 유지하는 동안 계속 감지한다. 반면 edge-trigger는 0 에서 1로 변하거나, 1에서 0으로 변할 때에만 이를 감지한다.
edge-trigger(줄여서 ET)의 특성 때문에 epoll 방식을 ET로 설정할 경우, 항상 non-blocking 방식으로 구현해야 한다. 예를 들어서 소켓이 데이터를 전송했을 때, LT방식이라면 데이터가 있다(1)고 감지하는 동안 계속 버퍼에서 내용을 읽어들일 수 있지만, ET의 경우 데이터가 변경된 시점(즉, 버퍼로 데이터가 전송된 순간)에만 이벤트를 감지하기 때문에, 버퍼에서 내용을 읽어들이다가 block된 경우 나머지 데이터를 읽어올 수 없기 때문이다.
* LT와 ET를 이해하기 어려워서 애를 많이 먹었는데, 가장 깔끔하게 설명된 페이지가 kldp에 있어서 공유합니다(https://kldp.org/node/74537)
Summary
- epoll은 커널 공간이 파일 디스크립터를 관리하여 select보다 빠른 이벤트 처리가 가능하다.
- epoll_create를 통해 epoll 구조체를 생성하고, epoll_ctl을 통해 파일 디스크립터를 등록, 수정, 삭제하고, epoll_wait를 통해 파일 디스크립터의 변화를 탐지한다.
references
https://en.wikipedia.org/wiki/Epoll
epoll - Wikipedia
epoll is a Linux kernel system call for a scalable I/O event notification mechanism, first introduced in version 2.5.44 of the Linux kernel.[1] Its function is to monitor multiple file descriptors to see whether I/O is possible on any of them. It is meant
en.wikipedia.org
https://rammuking.tistory.com/entry/Epoll의-기초-개념-및-사용-방법
Epoll의 기초 개념 및 사용 방법
■ EPOLL ? Epoll은 리눅스에서 select의 단점을 보완하여 사용할 수 있도록 만든 I/O통지 모델이다. 파일 디스크립터를 사용자가 아닌 커널이 관리를 하며, 그만큼 CPU는 계속해서 파일 디스크립터의
rammuking.tistory.com
https://jvns.ca/blog/2017/06/03/async-io-on-linux--select--poll--and-epoll/
Async IO on Linux: select, poll, and epoll
This week I got a new book in the mail: The Linux Programming Interface. My awesome coworker Arshia recommended it to me so I bought it! It’s written by the maintainer of the Linux man-pages project, Michael Kerrisk. It talks about the Linux programming
jvns.ca
댓글