do_all_file_descriptor_in_linux_represent_io - nutthawit/alpine-dotfile GitHub Wiki
Excellent and subtle question — the answer is “almost all, but not strictly only I/O in the traditional sense.”
Let’s go step by step 👇
In Linux (and all Unix-like OSes):
A file descriptor (FD) is simply a small integer that identifies an open kernel object associated with a process.
Each process has its own FD table, mapping numbers (0, 1, 2, 3, …) → to kernel objects.
So yes, most of them represent I/O endpoints, but not all are “files” in the usual sense.
| Category | Example | Description | I/O-like? |
|---|---|---|---|
| Regular files |
/etc/passwd, logs, etc. |
Classic disk I/O | ✅ Yes |
| Pipes / FIFOs | Anonymous or named pipes | Data streaming between processes | ✅ Yes |
| Sockets | TCP/UDP/UNIX sockets | Network or IPC communication | ✅ Yes |
| Terminals / PTYs |
/dev/tty, pseudo-terminals |
Used by shells, SSH, etc. | ✅ Yes |
| Character devices |
/dev/input/event0, /dev/urandom
|
Interface to hardware devices | ✅ Yes |
| Block devices | /dev/sda |
Used for disk I/O | ✅ Yes |
| Event FDs |
eventfd, timerfd, signalfd, inotify
|
Used for signaling / events | ⚙️ Not "data I/O", but I/O-ready |
| epoll FDs | Returned by epoll_create()
|
Used to monitor other FDs | ⚙️ Special kernel event interface |
| memfd / tmpfs | Anonymous memory-backed “files” | Shared memory / IPC | ✅ (acts like file I/O) |
| fanotify / netlink | File change or networking notification | Kernel messaging channel | ⚙️ System event I/O |
Even though some FDs (like eventfd, timerfd, epoll) don’t represent "reading or writing data" in the traditional sense, they still expose an I/O interface:
- You can
read()them (to consume events), -
poll()orepoll_wait()can wait until they’re “readable.”
That’s why calloop and other event loops can monitor them all the same way — they all fit into the I/O readiness model (readable, writable, error).
You can create a timer like this:
int fd = timerfd_create(CLOCK_MONOTONIC, 0);Then you can use read(fd, &expirations, 8) to get how many times it fired.
No "real data" is transferred — yet it behaves exactly like a readable file.
So it’s not “I/O” in the sense of disk or network data,
but it is I/O in the sense of the Unix I/O abstraction — a file descriptor you can read(), poll(), or select().
This design is intentional and fundamental to Unix philosophy:
“Everything is a file.”
It means:
- Devices, sockets, timers, events — all are accessible through the same API:
read,write,poll, etc. - You can multiplex them with the same event loop (like
calloop,epoll,select).
That’s why in Rust and other languages, everything that implements AsFd / AsRawFd can integrate seamlessly with I/O polling.
| Statement | True? | Explanation |
|---|---|---|
| “All FDs are for I/O.” | ✅ Mostly true | They all expose an I/O interface (read/write/poll), even if not classic data I/O |
| “All FDs represent files.” | ✅ Conceptually yes | In Unix, everything is modeled as a file |
| “Some FDs are for events, not data.” | ✅ | e.g., timerfd, signalfd, epoll
|
| “FDs are always readable/writable.” | Some are only readable (like timerfd), some only writable |
If you’d like, I can show you a categorized map of all FD types available in /proc/<pid>/fd, and how each fits into the I/O model (read/write/poll behavior). Would you like that?