1. Repository
2. What is I/O Multiplexing?
I/O Multiplexing is a technique that allows a single thread to monitor multiple file descriptors (sockets, files, pipes) simultaneously, waiting for any of them to become ready for I/O operations.
The Problem Without Multiplexing:
The Solution With Multiplexing using select:
Here we are blocking our program execution using select. Different from accept, there are multiple ways to unblock this select sys call.
3. The select() System Call
select() System Call3.1. Purpose
select is used to monitor multiple file descriptors simultaneously, waiting for any to become ready for I/O (such as new connection, new data received, disconnection of a socket, etc).
3.2. Function Signature
Returns:
- Number of ready file descriptors (> 0)
- 0 if timeout expired
- -1 on error
What it does: Blocks until at least one file descriptor in the specified sets is ready for I/O, or until timeout.
3.3. How select() Enables Multiplexing
Key Concept: select() blocks on multiple file descriptors simultaneously, not just one.
3.4. Parameters Explained
3.4.1. nfds - The Range to Monitor
-
nfdsis NOT the count of file descriptors! -
nfds= (highest file descriptor number) + 1 -
select()will check all file descriptors from 0 to (nfds - 1).
Adding by 1 simply because it specifies the range upper bound (exclusive), not the actual highest fd.
3.4.2. readfds - Sockets to Monitor for Reading
A set of file descriptors to monitor for "ready to read" status:
- For listening sockets: ready when a new connection is available
- For client sockets: ready when data is available to read
3.4.3. writefds - Sockets to Monitor for Writing (optional)
A set of file descriptors to monitor for "ready to write" status. Can be NULL if not needed.
3.4.4. exceptfds - Sockets to Monitor for Exceptions (optional)
A set of file descriptors to monitor for exceptional conditions. Usually NULL.
3.4.5. timeout - How Long to Wait (optional)
NULL: Block indefinitely until activity- Set value: Block for specified time
{0, 0}: Return immediately (polling mode)
4. File Descriptor Sets (fd_set)
fd_set)To work with select(), we need to manage sets of file descriptors using these macros:
select() modifies the fd_set to indicate which descriptors are ready. We must rebuild the sets on each loop iteration by using our state variable:
5. Complete Multiplexing Example
Here's a server that handles multiple clients using select():
6. Visualizing select() in Action
select() in ActionHere's a trace from a real server showing multiplexing:
Notice:
-
nfdsincreases as more clients connect (4 → 5 → 6) -
The
fd_setshows which sockets are being watched[3]→[3 4]→[3 4 5] -
select()returns when any socket has activity (such as new data, new connection, new disconnection) -
The server handles new connections AND existing client data seamlessly
7. The Blocking Paradox Explained
Question. If select() blocks, how does it handle multiple connections?
The answer: select() gets unblocked efficiently on all sockets at once.
7.1. Traditional Blocking (No Multiplexing)
7.2. Multiplexed Blocking with select()
Analogy: Think of select() like a receptionist watching multiple phone lines:
- The receptionist waits (blocks)
- But they're watching all phones simultaneously
- When any phone rings, they wake up and answer it
- Then go back to watching all phones again
This is much better than having one receptionist per phone (one thread per connection)!
8. Why Multiplexing Matters
8.1. Without select() - Poor Solutions
Option 1: Sequential blocking (doesn't work)
Option 2: One thread per connection (doesn't scale)
Option 3: Busy polling (wastes CPU)
8.2. With select() - Elegant Solution
Benefits:
- Single thread handles many connections
- No wasted CPU (only wake when needed)
- Handles new connections AND existing clients
- Scales to hundreds of connections
9. When to Use select()
select()Good for:
-
Servers handling multiple concurrent clients
-
Programs monitoring multiple I/O sources (sockets, files, pipes)
-
When we need portability (select works on all Unix-like systems)
Alternatives:
-
poll()- similar to select, but better interface for many fds -
epoll()(Linux) - more efficient for thousands of connections -
kqueue()(BSD/macOS) - BSD's equivalent to epoll
10. Summary
-
select()is a blocking multiplexer. It blocks, but on multiple file descriptors simultaneously -
nfdsis highest fd + 1. This number tells select() the range to scan[0, nfds-1] -
fd_setmust be rebuilt each iteration.select()modifies it to showfds that are ready -
Use
FD_ISSET()afterselect()returns. This is to check which specific fds are ready -
Multiplexing enables single-threaded concurrency. By using
selectwe have achieved the handling of multiple clients without threads -
The OS does the heavy lifting. We let kernel monitor all sockets efficiently, wakes our process only when needed








