Skip to content

Day49 - signalfd + epoll (Graceful Shutdown)

Overview

In this lab, we integrate Linux signalfd into the epoll-based TCP server.

This allows the server to:

  • Handle signals (SIGINT / SIGTERM) as file descriptor events
  • Avoid traditional signal handlers
  • Perform graceful shutdown inside the main event loop

Why signalfd?

Traditional signal handling:

signal(SIGINT, handler);

Problems:

  • asynchronous execution context
  • limited safe APIs
  • race conditions
  • difficult to integrate with event-driven design

Core Concept

signal → file descriptor → epoll event

Instead of:

signal → interrupt → handler

Key APIs

1. sigset_t

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);

Defines which signals we want to handle.


2. sigprocmask()

sigprocmask(SIG_BLOCK, &mask, NULL);

Block signals so they are not handled by default behavior.


3. signalfd()

fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);

Creates a file descriptor that receives signal events.


4. read()

read(signal_fd, &siginfo, sizeof(siginfo));

Reads signal events.


Important Behavior

  • Signals must be blocked (sigprocmask) before using signalfd
  • signalfd becomes readable when a signal is pending
  • read() must be called to clear the event
  • otherwise epoll will continuously trigger

Integration with epoll

We convert all event sources into epoll_item:

EPOLL_ITEM_SERVER
EPOLL_ITEM_CLIENT
EPOLL_ITEM_TIMER
EPOLL_ITEM_SIGNAL

Event Flow

Ctrl+C (SIGINT)
kernel queues signal
signalfd becomes readable
epoll_wait()
read(signalfd)
graceful shutdown

Graceful Shutdown Design

if (siginfo.ssi_signo == SIGINT ||
    siginfo.ssi_signo == SIGTERM) {
    return 0; // exit event loop
}

Main loop exits:

server_run()  return
main()  server_cleanup()

Resource Cleanup

All resources are released in order:

  • client list
  • epoll items
  • timerfd
  • signalfd
  • epoll fd
  • server socket

Comparison with timerfd

Feature timerfd signalfd
Source time signal
Trigger periodic external event
read() expiration count signal info

Summary

This lab completes the event-driven model:

  • network I/O → socket
  • timer → timerfd
  • signal → signalfd

All unified under epoll.