들어가며
전통적인 select나 poll 방식은 감시할 파일 디스크립터(FD)가 많아질수록 성능이 저하되는 O(N)의 한계를 가집니다. 하지만 epoll은 이러한 단점을 극복하고, 감시 대상의 수에 관계없이 O(1)의 시간 복잡도로 이벤트 발생을 감지할 수 있게 해줍니다. 이번 시간에는 리눅스의 대표적인 I/O 멀티플렉싱 메커니즘, epoll의 내부 동작 원리를 함께 살펴보겠습니다.
먼저 recv()는 어떻게 잠들고 깨어날까? 를 보고 오시면 비슷한 메커니즘 이기때문에, 더 잘이해가 갈 수 있습니다.
커널 코드의 모든 세부사항을 다루기보다는, epoll이 어떤 아이디어로 설계되었고, 주요 함수들이 어떻게 상호작용하는지 명확하게 이해하는 것을 목표로 합니다.
😃 이론: epoll, 무엇이 다른가?
epoll의 핵심 아이디어는 간단합니다. 여러 소켓(파일 디스크립터)을 동시에 감시하다가, 실제로 데이터가 준비된(이벤트가 발생한) 소켓에 대한 정보만 애플리케이션에 알려주는 것입니다.
그렇다면 epoll은 주로 어떻게 활용될까요?
가장 대표적인 예시는 Java의 Tomcat NIOConnector입니다. Tomcat에서는 단일 스레드 (또는 소수의 스레드)가 epoll_wait()를 호출하며 이벤트 발생을 대기합니다. 그러다가 특정 소켓들에서 데이터 수신과 같은 이벤트가 감지되면, epoll은 해당 소켓들의 정보를 반환합니다. 그러면 Acceptor 스레드는 이 정보를 바탕으로 실제 I/O 작업을 처리할 워커 스레드(일반적으로 스레드 풀에서 관리되는)에게 작업을 위임합니다. 이러한 구조 덕분에 적은 수의 스레드로도 수많은 동시 연결을 효율적으로 관리할 수 있는 것입니다.
핵심 구성 요소와 상호작용
epoll 메커니즘이 효과적으로 동작하기 위해서는 몇 가지 핵심 구성 요소들이 유기적으로 연결되어야 합니다.

위 그림은 epoll의 핵심적인 상호작용을 잘 보여줍니다.
- 소켓(Socket)과 epoll 인스턴스(Epoll 구조체): 이 둘은 서로를 참조할 수 있어야 합니다. 소켓에서 이벤트가 발생하면 자신이 어떤
epoll인스턴스에 등록되어 있는지 알아야 하고,epoll인스턴스는 자신이 감시하는 소켓들의 목록을 가지고 있어야 합니다. - Epoll 구조체와 대기 중인 스레드(Sleeping Thread):
epoll인스턴스는epoll_wait()를 호출하고 이벤트 발생을 기다리며 잠들어 있는 스레드(들)에 대한 정보를 유지합니다. - 이벤트 발생 시나리오: 만약 그림의 소켓 1과 소켓 3에서 동시에 데이터가 수신되었다고 가정해 봅시다. 각 소켓은 자신에게 이벤트가 발생했음을 커널 내부적으로 알리고, 연결된
epoll구조체는 이 정보를 취합합니다. 그리고epoll구조체는 대기 큐에서 잠자고 있던 스레드를 깨워, "소켓 1과 소켓 3에서 이벤트가 발생했으니 확인해보세요!" 라고 알려줍니다.
언뜻 보기에는 그리 복잡하지 않아 보입니다. 하지만 이 간단한 아이디어를 효율적으로 구현하기 위해 리눅스 커널 내부에서는 정교한 자료구조와 메커니즘이 사용됩니다. 이제 실제 리눅스 커널 코드(v5.15 기준)를 살펴보며 이 흐름을 따라가 보겠습니다. 전체적인 흐름 이해를 돕기 위해 일부 추상화와 생략이 있을 수 있으니, 정말 깊이 있는 동작 방식이 궁금하시다면 직접 커널 코드를 분석해보시는 것을 강력히 추천합니다.
환경
Linux 커널 5.15

📞 Epoll 관련 주요 구조체
epoll의 커널 코드를 본격적으로 살펴보기 전에, 핵심적인 역할을 하는 구조체들을 먼저 이해하면 전체적인 그림을 그리기가 한결 수월해집니다. eventpoll, epitem, epoll_event 이 세 가지가 가장 중요합니다.
1. eventpoll 구조체: epoll 인스턴스의 본체
eventpoll 구조체는 epoll 인스턴스 그 자체를 나타내는 핵심 데이터 구조입니다. epoll_create() 시스템 콜을 통해 생성되며, epoll의 모든 작업을 관장합니다.
struct eventpoll {
wait_queue_head_t wq;
struct rb_root_cached rbr;
struct list_head rdllist;
...
}
여기서 주목해야 할 주요 필드는 다음과 같습니다.
wait_queue_head_t wq: epoll_wait()을 호출한 스레드들이 이벤트를 기다리며 잠드는 대기 큐입니다. 일반적으로 단일 스레드가 epoll을 대기하지만, 여러 스레드가 동일한 epoll 인스턴스를 대기할 수도 있습니다.
struct rb_root_cached rbr: epoll 인스턴스가 감시 중인 모든 파일 디스크립터(FD)에 대한 정보(epitem)들을 저장하는 레드-블랙 트리입니다.
struct list_head rdllist: 'Ready List'의 약자로, 이벤트가 발생한 FD(epitem)들이 임시로 추가되는 연결 리스트입니다. 소켓에서 이벤트가 발생하면 해당 소켓에 연결된 epitem이 이 리스트에 추가되고, epoll_wait()는 이 리스트를 확인하여 사용자에게 알릴 이벤트를 결정합니다.
이 구조를 통해 eventpoll 인스턴스가 하는 일을 간략히 정의할 수 있습니다. 평소에는 wq 대기 큐에 연결된 스레드와 함께 대기 상태에 있다가, rdllist에 새로운 이벤트(epitem)가 등록되면, wq에서 잠자던 스레드를 깨워 rdllist의 내용을 사용자 공간으로 전달하는 역할을 합니다.
2. epitem 구조체: 감시 대상 FD의 대리자
epitem 구조체는 epoll이 감시하는 개별 파일 디스크립터 하나하나를 대표하는 구조체입니다. 각 epitem은 특정 FD와 그 FD에서 감시할 이벤트, 그리고 해당 FD가 속한 eventpoll 인스턴스 등에 대한 정보를 담고 있습니다.
struct epitem {
union {
struct rb_node rbn; struct rcu_head rcu;
};
struct list_head rdllink;
struct epoll_filefd ffd;
struct eventpoll *ep;
struct epoll_event event;
};
주요 필드는 다음과 같습니다.
struct rb_node rbn: eventpoll 구조체의 rbr (레드-블랙 트리)에 이 epitem을 삽입하기 위한 노드입니다.
struct list_head rdllink: 해당 epitem에 연결된 FD에서 이벤트가 발생했을 때, eventpoll 구조체의 rdllist에 연결되기 위한 링크입니다.
struct epoll_filefd ffd: 감시 대상 파일 디스크립터에 대한 정보를 담습니다. 내부적으로 struct file 포인터와 int fd 값을 가집니다.
struct epoll_filefd {
struct file *file;
int fd;
}
struct eventpoll *ep: 이 epitem이 속해 있는 부모 eventpoll 인스턴스를 가리키는 포인터입니다.
struct epoll_event event: 사용자가 이 FD에 대해 어떤 이벤트를 감시하고 싶은지, 그리고 이벤트 발생 시 사용자에게 전달할 데이터를 정의합니다. (아래 epoll_event 구조체에서 자세히 설명)
3. epoll_event 구조체: 사용자에게 전달될 이벤트 정보
epoll_event 구조체는 사용자 공간과 커널 공간 사이에서 이벤트 정보를 주고받는 데 사용됩니다. 사용자는 epoll_ctl()을 통해 감시할 이벤트 유형과 관련 데이터를 커널에 전달하고, epoll_wait()는 발생한 이벤트 정보를 이 구조체에 담아 사용자에게 반환합니다.
struct epoll_event {
__poll_t events;
__u64 data;
}
events: 감지하고자 하는 이벤트의 종류를 나타내는 비트마스크입니다. (예: EPOLLIN - 읽기 가능, EPOLLOUT - 쓰기 가능, EPOLLET - 엣지 트리거 방식 등)
data: 사용자 정의 데이터입니다. epoll_wait()이 이벤트를 반환할 때, 이 data 필드도 함께 전달되어 사용자가 어떤 FD에서 이벤트가 발생했는지 식별하거나 추가 정보를 활용하는 데 도움을 줍니다. 주로 FD 자체를 넣거나, FD와 관련된 객체의 포인터를 넣는 경우가 많습니다.
구조체들의 역할을 종합해보면 다음과 같습니다.
사용자가 epoll_create()로 eventpoll 인스턴스를 만들고, epoll_ctl()로 감시할 FD들을 등록하면 각 FD에 대한 epitem이 생성되어 eventpoll의 레드-블랙 트리에 저장됩니다. 이때 각 epitem에는 사용자가 지정한 epoll_event 정보도 함께 저장됩니다. 만약 특정 FD에서 이벤트가 발생하면, 해당 FD와 연결된 epitem이 eventpoll의 rdllist에 추가됩니다. epoll_wait()를 호출하고 대기 중이던 스레드는 깨어나 rdllist를 확인하고, 여기에 등록된 epitem들의 epoll_event 정보를 사용자 공간의 epoll_event 배열로 복사하여 반환합니다.
이제 epoll의 핵심 동작을 대표하는 세 가지 시스템 콜, epoll_create, epoll_ctl, epoll_wait가 내부적으로 어떻게 이 구조체들을 활용하는지 살펴보겠습니다.
🐆 epoll_create: epoll 인스턴스의 탄생
epoll_create() 시스템 콜은 새로운 epoll 인스턴스를 생성하고, 이 인스턴스를 가리키는 파일 디스크립터(epfd)를 반환합니다. 이 epfd는 이후 epoll_ctl(), epoll_wait() 호출 시 해당 epoll 인스턴스를 식별하는 데 사용됩니다.
커널 내부에서는 do_epoll_create() 함수가 이 작업을 수행합니다.
커널 소스
static int do_epoll_create(int flags)
{
int error, fd;
struct eventpoll *ep = NULL;
struct file *file;
error = ep_alloc(&ep);
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, O_RDWR | (flags & O_CLOEXEC));
fd_install(fd, file);
return fd;
}
주요 단계를 살펴보면:error = ep_alloc(&ep);
앞서 설명한 eventpoll 구조체에 대한 메모리를 할당하고, 내부 필드(wq, rbr, rdllist 등)를 초기화하여 ep 포인터에 할당합니다. 이로써 epoll 인스턴스의 실체가 커널 내부에 마련됩니다.
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
새로운 epoll 인스턴스를 참조할 파일 디스크립터(epfd)를 할당받습니다.
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, O_RDWR | (flags & O_CLOEXEC));
"eventpoll"이라는 이름으로 익명 inode(실제 파일 시스템 경로가 없는 inode)를 만들고, 이에 대한 struct file 객체를 생성합니다.
이 file 객체의 private_data 필드에는 앞서 생성한 eventpoll 구조체의 포인터가 저장됩니다. 이렇게 함으로써, 나중에 epfd와 file 객체를 통해 eventpoll 구조체에 접근할 수 있게 됩니다.
fd_install(fd, file);
앞서 할당받은 fd(epfd)와 생성된 file 객체를 현재 프로세스의 파일 디스크립터 테이블에 연결합니다. 이제 사용자 공간에서는 이 fd를 통해 epoll 인스턴스를 조작할 수 있게 됩니다.
여기서 한 가지 의문이 들 수 있습니다. "단순히 eventpoll 구조체만 있으면 될 것 같은데, 왜 굳이 파일 시스템과 연관된 file 객체를 만들고 FD까지 할당하는 걸까요?"
여기에는 리눅스의 핵심 철학이 담겨 있습니다.
1. "Everything is a file" 철학의 일관성: 리눅스는 파이프, 소켓, 장치 등 다양한 커널 객체를 파일처럼 다룹니다. epoll 인스턴스 역시 파일 디스크립터를 통해 접근하고 관리함으로써 이러한 일관성을 유지합니다.
2. 계층적 epoll (Nested epoll): epoll 인스턴스 자체도 하나의 파일 디스크립터로 표현되므로, 다른 epoll 인스턴스의 감시 대상이 될 수 있습니다. 즉, epfd를 또 다른 epfd에 등록하여 epoll의 이벤트를 다른 epoll이 감지하는 것이 가능합니다. 이는 복잡한 이벤트 처리 구조를 설계할 때 유용할 수 있습니다.
🍧 epoll_ctl: 감시 목록 편집하기
epoll_ctl() 시스템 콜은 epoll_create()로 생성된 epoll 인스턴스에 감시할 파일 디스크립터(FD)를 추가(EPOLL_CTL_ADD), 수정(EPOLL_CTL_MOD), 또는 삭제(EPOLL_CTL_DEL)하는 데 사용됩니다.
int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
bool nonblock)
{
int error;
struct fd f, tf;
struct eventpoll *ep;
struct epitem *epi;
f = fdget(epfd);
tf = fdget(fd);
ep = f.file->private_data;
switch (op) {
case EPOLL_CTL_ADD:
error = ep_insert(ep, epds, tf.file, fd, full_check);
break;
case EPOLL_CTL_DEL:
//...
break;
case EPOLL_CTL_MOD:
// ...
break;
}
return error;
}
핵심 로직을 따라가 보겠습니다.f = fdget(epfd);: 사용자가 전달한 epfd(epoll 인스턴스의 파일 디스크립터)를 이용해 커널 내 struct file 객체를 가져옵니다.tf = fdget(fd);: 감시 대상으로 등록할 fd(예: 소켓의 파일 디스크립터)를 이용해 해당 struct file 객체를 가져옵니다.
ep = f.file->private_data;: epfd에 해당하는 file객체의 private_data 필드에서 eventpoll 구조체의 주소를 얻습니다. 이것이 우리가 조작하려는 epoll 인스턴스입니다.
op (operation) 인자에 따라 분기합니다. 여기서는 EPOLL_CTL_ADD (새로운 FD 등록) 경우를 중심으로 살펴보겠습니다.
ep_insert() 함수를 호출하여 새로운 epitem을 생성하고 epoll 인스턴스에 등록합니다.
ep_insert: 감시 대상 FD를 epoll에 연결하는 핵심 과정
ep_insert() 함수는 EPOLL_CTL_ADD 요청 시 실질적인 등록 작업을 수행하는 매우 중요한 함수입니다.
static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
struct file *tfile, int fd, int full_check)
{
int error = 0;
__poll_t revents;
struct epitem *epi;
struct ep_pqueue epq;
//epitem 구조체 생성 및 초기화
epi = kmem_cache_zalloc(epi_cache, GFP_KERNEL);
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
epi->ep = ep;
ep_set_ffd(&epi->ffd, tfile, fd);
epi->event = *event;
//감시 대상 file 에 epitem 등록
attach_epitem(tfile, epi);
// rbr 트리에 epitem 등록
ep_rbtree_insert(ep, epi);
//콜백 함수 등록
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
revents = ep_item_poll(epi, &epq.pt, 1);
return 0;
}
먼저 epitem 구조체를 생성하고 내부 값들을 초기화 해줍니다. 이 과정에서 사용자가 정의한 epoll_event.data 값을 epitem 에 넣어주게 됩니다. 이 값은 해당 소켓을 식별하기 위한 용도로 사용되므로 보통 FD 값을 포함합니다.
attach_epitem(tfile, epi);
감시 대상의 file>f_ep 라는 필드에 epitem 을 등록해줍니다.이렇게 되면, 소켓 -> epitem -> epollevent 방향 연결이 완성됩니다.
ep_rbtree_insert(ep, epi);
생성된 epitem을 eventpoll 인스턴스의 레드-블랙 트리(rbr)에 삽입합니다.
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
revents = ep_item_poll(epi, &epq.pt, 1);
소켓에 콜백 함수를 등록해주는 부분입니다. 결과적으로, 이 과정을 통해 감시 대상 파일의 이벤트 발생 시 ep_poll_callback 함수가 호출되도록 설정됩니다.
요약하자면, ep_insert는 epitem을 생성하여 eventpoll의 자료구조에 등록하고, 가장 중요하게는 감시 대상 파일에 이벤트가 발생했을 때 ep_poll_callback 함수가 호출되도록 콜백을 설정하는 역할을 합니다.
🐠 epoll_wait: 이벤트 발생을 기다리다
epoll_wait() 시스템 콜은 epoll 메커니즘의 사용자 대기 인터페이스입니다. 이 함수를 호출한 스레드는 epoll 인스턴스가 감시하는 파일 디스크립터 중 하나라도 이벤트가 발생할 때까지 블로킹됩니다. 이벤트가 발생하면, 발생한 이벤트 정보를 담은 epoll_event 구조체 배열을 사용자에게 전달하고, 발생한 이벤트의 수를 반환합니다.
내부적으로 epoll_wait()는 ep_poll() 함수를 호출합니다.
커널 소스
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, struct timespec64 *timeout)
{
int res = 0, eavail, timed_out = 0;
wait_queue_entry_t wait;
while (1) {
//새로운 이벤트가 발생한 경우
if (eavail) {
res = ep_send_events(ep, events, maxevents);
if (res)
return res;
}
//이벤트가 발생했는지 확인한다.
eavail = ep_events_available(ep);
//이벤트가 발생하지 않으면 sleep 한다.
if (!eavail) {
__add_wait_queue_exclusive(&ep->wq, &wait);
timed_out = !schedule_hrtimeout_range(to, slack,HRTIMER_MODE_ABS);
}
__set_current_state(TASK_RUNNING);
}
}
ep_poll 함수의 동작은 크게 두 부분으로 나눌 수 있습니다:
이벤트 대기: eventpoll의 rdllist (준비된 이벤트 목록)를 확인하고, 이벤트가 없으면 스레드를 재웁니다.
이벤트 전달: rdllist에 이벤트가 있으면, 해당 이벤트 정보를 사용자 공간의 events 배열로 복사합니다.
ep_send_events: 준비된 이벤트를 사용자에게 전달
ep_send_events 함수는 ep_poll 내부에서 호출되며, eventpoll의 rdllist에 쌓인 "준비된" epitem들을 순회하며, 그 정보를 사용자 공간의 epoll_event 배열로 복사하는 역할을 담당합니다.
static int ep_send_events(struct eventpoll *ep,
struct epoll_event __user *events, int maxevents)
{
struct epitem *epi, *tmp;
int res = 0;
ep_start_scan(ep, &txlist);
list_for_each_entry_safe(epi, tmp, &txlist, rdllink) {
__poll_t revents;
revents = ep_item_poll(epi, &pt, 1);
events = epoll_put_uevent(revents, epi->event.data, events);
res++;
}
return res;
}
ep_start_scan(ep, &txlist);eventpoll의 rdllist에 있는 epitem들을 txlist라는 임시 리스트로 옮깁니다. 이 과정은 보통 eventpoll의 락을 잡은 상태에서 수행되어 동시성 문제를 방지합니다.
list_for_each_entry_safe(epi, tmp, &txlist, rdllink)
txlist 순회하면서 각각의 epitem 을 확인합니다.
revents = ep_item_poll(epi, &pt, 1);
다시한번 해당 fd에 실제 이벤트가 있는지 확인 합니다. 이 revents는 파일 자체에서 보고하는 모든 가능한 이벤트 마스크입니다.
events = epoll_put_uevent(revents, epi->event.data, events);
이 함수가 핵심입니다. 인자로 있는 events 는 유저에게 전달되는 epoll_event 배열 입니다. epitem 내에 있는 내용을 바탕으로 epoll_event 구조체를 만들고 events 배열에 추가 해줍니다. 아까 ep_insert 에서 전달해둔 epitem 내부의epoll_event.data를 전달합니다.
ep_events_available
eventpoll의 rdllist가 비어있는지 (즉, 보고할 이벤트가 없는지) 확인합니다.
만약, 새로운 이벤트가 발행되지 않았다면 아래 블록이 실행됩니다.
if (!eavail) {
__add_wait_queue_exclusive(&ep->wq, &wait);
timed_out = !schedule_hrtimeout_range(to, slack,HRTIMER_MODE_ABS);
}
__add_wait_queue_exclusive(&ep->wq, &wait);
현재 스레드를 eventpoll의 대기 큐(ep->wq)에 추가하고 잠들 준비를 합니다.
timed_out = !schedule_hrtimeout_range(to, slack,HRTIMER_MODE_ABS);
실제로 스레드를 재우는 부분입니다. 계속 함수를 따라가면 context_switch() 를 실행하게 됩니다.
epoll_wait의 전체 동작을 요약하면, rdllist에 보고할 이벤트가 있는지 확인하고, 없으면 잠듭니다. 이벤트가 있거나 잠에서 깨어나면, rdllist의 내용을 사용자 공간의 epoll_event 배열로 복사하여 전달합니다.
🙌 ep_poll_callback: 이벤트 발생을 epoll에게 알리는 시작
epoll_ctl()의 ep_insert() 과정에서 감시 대상 파일에 등록된 콜백 함수라고 설명했습니다. 즉, 해당 파일 디스크립터(예: 소켓의 수신 버퍼)에 데이터가 들어오는 등 관심 있는 이벤트가 발생하면 커널에 의해 이 함수가 호출됩니다.
static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, void *key)
{
//epitem, eventpoll 객체를 꺼낸다.
struct epitem *epi = ep_item_from_wait(wait);
struct eventpoll *ep = epi->ep;
//rdllist 에 epitem 을 달아준다.
list_add_tail_lockless(&epi->rdllink, &ep->rdllist);
//wq 에서 자고 있는 쓰레드를 깨워준다.
wake_up(&ep->wq);
}
struct epitem *epi = ep_item_from_wait(wait);
콜백을 호출한 wait_queue_entry_t로부터 이 엔트리와 연결된 epitem을 찾아냅니다. (이 연결은 ep_insert 시점에 설정됩니다.)
struct eventpoll *ep = epi->ep;
epitem을 통해 부모 eventpoll 인스턴스의 포인터를 얻습니다.
list_add_tail_lockless(&epi->rdllink, &ep->rdllist);
해당 epitem을 eventpoll의 rdllist (준비된 목록)의 꼬리에 추가합니다.
wake_up(&ep->wq);
eventpoll의 대기 큐(ep->wq)에서 잠자고 있는 스레드(들)를 깨웁니다.
이 콜백 함수 덕분에 epoll_wait()는 모든 FD를 일일이 스캔할 필요 없이, 이벤트가 실제로 발생한 FD의 정보만 효율적으로 얻을 수 있습니다.
📒 그림 요약
1. Epoll 과 소켓 관계

2. Socket 내부로 데이터가 들어올시

3. User 공간으로 event 발생 내용 복사

마치며: epoll, I/O Multiplexing
지금까지 리눅스 epoll의 내부 구조와 핵심 함수들의 동작 원리를 살펴보았습니다. 요약하자면, epoll의 효율성은 다음과 같은 설계에서 비롯됩니다.
1. 콜백 기반 이벤트 감지: 각 감시 대상 파일에 ep_poll_callback이라는 콜백 함수를 등록해두고, 이벤트가 발생하면 해당 파일 시스템(또는 소켓 스택)이 직접 이 콜백을 호출합니다.
2. 준비된 목록 (rdllist): ep_poll_callback은 이벤트가 발생한 epitem을 eventpoll의 rdllist에 추가합니다.
3. 효율적인 대기 및 통지: epoll_wait()는 rdllist가 비어있으면 잠들고, rdllist에 항목이 추가되면 깨어나 해당 이벤트들만 epoll_event[]을 통해 사용자에게 전달합니다.
이러한 메커니즘 덕분에 epoll은 감시하는 파일 디스크립터의 수에 크게 구애받지 않고 일관된 성능으로 이벤트를 처리할 수 있으며, 이는 대규모 동시 접속을 처리해야 하는 현대의 백엔드 서버 애플리케이션에 있어 필수적인 기술로 자리매김하게 했습니다.
물론 실제 커널 코드는 여기서 설명한 것보다 훨씬 복잡하며, 다양한 엣지 케이스, 최적화, 락킹 메커니즘을 포함하고 있습니다. 하지만 이 글을 통해 epoll의 기본적인 아이디어와 전체적인 흐름을 이해하는 데 도움이 되셨기를 바랍니다.
Reference
https://rammuking.tistory.com/entry/Epoll%EC%9D%98-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95
https://github.com/torvalds/linux/blob/v5.15/include/uapi/linux/eventpoll.h
https://github.com/torvalds/linux/blob/v5.15/fs/eventpoll.c
'CS > Linux' 카테고리의 다른 글
| 리눅스는 어떻게 TLB Flush를 최적화할까? (0) | 2025.06.11 |
|---|---|
| [Linux] recv()는 어떻게 잠들고 깨어날까? (0) | 2025.05.23 |
| [Linux] 네트워크 커널 스택: 리눅스 커널 네트워크 패킷 수신 여정 #3 (0) | 2025.05.22 |
| [Linux] Ring Buffer 에서 NAPI: 리눅스 커널 네트워크 패킷 수신 여정 #2 (0) | 2025.05.21 |
| [Linux] NIC에서 링 버퍼까지: 리눅스 커널 네트워크 패킷 수신 여정 #1 (0) | 2025.05.20 |