Linux IO Multiplexing, Select vs Poll vs Epoll

介绍

One basic concept of Linux (actually Unix) is the rule that everything in Unix/Linux is a file. Each process has a table of file descriptors that point to files, sockets, devices and other operating system objects.

Typical system that works with many IO sources has an initializaion phase and then enter some kind of standby mode – wait for any client to send request and response it.

Simple solution is to create a thread (or process) for each client , block on read until a request is sent and write a response. This is working ok with a small amount of clients but if we want to scale it to hundred of clients, creating a thread for each client is a bad idea.

File Descriptor:

A file descriptor is a number that uniquely identifies an open file in a computer’s operating system. It describes a data resource, and how that resource may be accessed.

When a program asks to open a file — or another data resource, like a network socket — the kernel:

  • Grants access.
  • Creates an entry in the global file table.
  • Provides the software with the location of that entry.

The descriptor is identified by a unique non-negative integer, such as 0, 12, or 567. At least one file descriptor exists for every open file on the system.

File descriptors were first used in Unix, and are used by modern operating systems including Linux, macOS, and BSD. In Microsoft Windows, file descriptors are known as file handles.

When a process makes a successful request to open a file, the kernel returns a file descriptor which points to an entry in the kernel’s global file table. The file table entry contains information such as the inode of the file, byte offset, and the access restrictions for that data stream (read-only, write-only, etc.).

http://www.dabeaz.com/python/UnderstandingGIL.pdf

On a Unix-like operating system, the first three file descriptors, by default, are STDIN (standard input), STDOUT (standard output), and STDERR (standard error).

Name File descriptor Description Abbreviation
Standard input 0 The default data stream for input, for example in a command pipeline. In the terminal, this defaults to keyboard input from the user. stdin
Standard output 1 The default data stream for output, for example when a command prints text. In the terminal, this defaults to the user’s screen. stdout
Standard error 2 The default data stream for output that relates to an error occurring. In the terminal, this defaults to the user’s screen. stderr
1
find / -name '*something*' 2>/dev/null

The errors sent to /dev/null, and are not displayed.

IO Multiplexing

The solution is to use a kernel mechanism for polling over a set of file descriptors. There are 3 options you can use in Linux:

  • select(2)
  • poll(2)
  • epoll

All the above methods serve the same idea, create a set of file descriptors , tell the kernel what would you like to do with each file descriptor (read, write, ..) and use one thread to block on one function call until at least one file descriptor requested operation available

Select System Call

The select( ) system call provides a mechanism for implementing synchronous multiplexing I/O

sh int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

A call to select( ) will block until the given file descriptors are ready to perform I/O, or until an optionally specified timeout has elapsed

The watched file descriptors are broken into three sets:

  • File descriptors listed in the readfds set are watched to see if data is available for reading.
  • File descriptors listed in the writefds set are watched to see if a write operation will complete without blocking.
  • File descriptors in the exceptfds set are watched to see if an exception has occurred, or if out-of-band data is available (these states apply only to sockets).

Select – summary:

  • We need to build each set before each call
  • The function check any bit up to the higher number – O(n)
  • We need to iterate over the file descriptors to check if it exists on the set returned from select
  • The main advantage of select is the fact that it is very portable – every unix like OS has it

Poll system call

Unlike select(), with its inefficient three bitmask-based sets of file descriptors, poll( ) employs a single array of nfds pollfd structures. the prototype is simpler:

1
int poll (struct pollfd *fds, unsigned int nfds, int timeout);

参考

https://devarea.com/linux-io-multiplexing-select-vs-poll-vs-epoll/#.Yxb5AHZBxD8

File descriptor:
https://www.computerhope.com/jargon/f/file-descriptor.htm#:~:text=A%20file%20descriptor%20is%20a,Grants%20access.

epoll python example:
http://scotdoyle.com/python-epoll-howto.html

select, poll, epoll:https://jvns.ca/blog/2017/06/03/async-io-on-linux--select--poll--and-epoll/