Setting Up epoll
Create and register an epoll set.
Setting Up epoll is a free C Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Creating an epoll Instance
Everything starts with an epoll instance, itself a file descriptor. Create one with epoll_create1().
The argument is a flags field; pass 0 for default behavior or EPOLL_CLOEXEC to close the descriptor automatically across exec(). The returned fd is what you pass to every other epoll call.
int epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1) { perror("epoll_create1"); exit(1); }epoll_create1 vs epoll_create
The older epoll_create(int size) took a size hint, but the kernel ignores it now; the only requirement was that it be positive.
Prefer epoll_create1(): it takes flags instead of a meaningless size and lets you request EPOLL_CLOEXEC atomically, avoiding a separate fcntl() call.
/* legacy form, size arg ignored by kernel */
int epfd = epoll_create(1);The epoll_event Struct
Interest and results are both expressed through struct epoll_event. It has an events bitmask and a data union.
The data union holds whatever you want returned when the event fires: commonly data.fd, but it can also be a data.ptr to a connection object you allocated.
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_fd;Registering with EPOLL_CTL_ADD
epoll_ctl() manages the interest list. Use the EPOLL_CTL_ADD operation to start watching a descriptor.
You pass the epoll fd, the operation, the target fd, and a pointer to your epoll_event. After this, epoll will report readiness for that descriptor.
struct epoll_event ev = { .events = EPOLLIN, .data.fd = listen_fd };
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev) == -1)
perror("epoll_ctl: add");Event Flags: EPOLLIN and EPOLLOUT
The events mask says what you care about. EPOLLIN means the descriptor is readable; EPOLLOUT means it is writable.
You typically watch EPOLLIN on a listening socket (to accept) and on client sockets (to read). Add EPOLLOUT only when you have buffered data that did not fully send.
ev.events = EPOLLIN | EPOLLOUT;Error and Hangup Flags
The kernel always reports certain conditions even if you did not request them: EPOLLERR for errors, EPOLLHUP for a hangup, and EPOLLRDHUP for a peer half-close.
Check these in your handler; ignoring EPOLLHUP on a dead socket leads to spinning in the event loop.
if (ev.events & (EPOLLERR | EPOLLHUP)) {
close(ev.data.fd);
continue;
}Modifying Interest with EPOLL_CTL_MOD
Once a descriptor is registered you can change its event mask without removing it. Use EPOLL_CTL_MOD.
A common pattern: a connection normally watches only EPOLLIN, then switches to EPOLLIN | EPOLLOUT when its send buffer fills, and back again once flushed.
ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = conn_fd;
epoll_ctl(epfd, EPOLL_CTL_MOD, conn_fd, &ev);Removing with EPOLL_CTL_DEL
When a connection closes, stop watching it with EPOLL_CTL_DEL. The final epoll_event argument is ignored and may be NULL on modern kernels.
Note that closing a descriptor automatically removes it from any epoll interest lists, but explicit deletion before close keeps your bookkeeping clear.
epoll_ctl(epfd, EPOLL_CTL_DEL, conn_fd, NULL);
close(conn_fd);Level-Triggered vs Edge-Triggered
By default epoll is level-triggered: as long as a descriptor stays readable, every epoll_wait() reports it. This is forgiving and easy.
Add the EPOLLET flag for edge-triggered mode: you are notified only when readiness changes, so you must drain the socket fully in a loop until EAGAIN.
ev.events = EPOLLIN | EPOLLET; /* edge-triggered */
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);Storing a Context Pointer
Instead of data.fd, you can store data.ptr pointing to a per-connection struct. When the event fires you get that pointer back directly, no lookup table needed.
Just remember the union: if you set data.ptr, you cannot also read data.fd from the same event.
struct conn *c = malloc(sizeof *c);
c->fd = conn_fd;
ev.events = EPOLLIN;
ev.data.ptr = c;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);Cleaning Up
The epoll instance is just a file descriptor, so you release it with close() when the server shuts down.
Closing the epoll fd frees the kernel's interest list. Any still-open monitored sockets remain open; close those separately to release their resources.
close(epfd);Quick Check
Test your grasp of the epoll setup API.
Recap
You create an instance with epoll_create1(), then manage interest via epoll_ctl() using ADD, MOD, and DEL. Each struct epoll_event carries an events mask and a data union.
Choose level-triggered for simplicity or EPOLLET for edge-triggered performance. Next we drive these pieces from an event loop.
Frequently asked questions
Is the “Setting Up epoll” lesson free?
Yes — the full text of “Setting Up epoll” is free to read here on the web, and the C Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C Academy course, upgrade to CoddyKit PRO.
What will I learn in “Setting Up epoll”?
Create and register an epoll set. You practise C Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C Academy?
No prior experience is required. C Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Setting Up epoll” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C Academy lesson?
Yes. Every C Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Blocking vs Non-Blocking I/O
- Setting Up epoll
- The Event Loop
- A Simple Echo Server