C Academy · Lesson

Setting Up epoll

Create and register an epoll set.

Lesson 2 of 413 steps

Setting Up epoll is a free C Academy lesson on CoddyKit. This is 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, and your progress syncs across the web and the CoddyKit app. The C Academy course includes 4 lessons in total.

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.

Free to start

Learn C with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
39
Lessons
144

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. 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. The C Academy course includes 4 lessons in total.

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, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.

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

  1. Blocking vs Non-Blocking I/O
  2. Setting Up epoll
  3. The Event Loop
  4. A Simple Echo Server
← Back to C Academy