select & poll
#include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); // Four macros closely combined with select: void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set);
select:
#include <stdio.h> #include <poll.h> int main() { struct pollfd fds[1]; int timeout = 5000; // set the timeout to 5 seconds fds[0].fd = 0; // standard input file descriptor is 0 fds[0].events = POLLIN; // focus on readable events while (1) { int ready = poll(fds, 1, timeout); // call the poll function to wait for the event to occur if (ready == -1) { perror("poll"); break; } else if (ready == 0) { printf("Timeout\\ "); } else { // check if standard input is ready if (fds[0]. revents & POLLIN) { char buffer[1024]; int bytesRead = read(0, buffer, sizeof(buffer)); if (bytesRead > 0) { // process data read from standard input printf("Read %d bytes: %.*s\\ ", bytesRead, bytesRead, buffer); } else if (bytesRead == 0) { printf("EOF\\ "); break; } else { perror("read"); break; } } } } return 0; }
poll:
#include <stdio.h> #include <poll.h> int main() { struct pollfd fds[1]; int timeout = 5000; // set the timeout to 5 seconds fds[0].fd = 0; // standard input file descriptor is 0 fds[0].events = POLLIN; // focus on readable events while (1) { int ready = poll(fds, 1, timeout); // call the poll function to wait for the event to occur if (ready == -1) { perror("poll"); break; } else if (ready == 0) { printf("Timeout\\ "); } else { // check if standard input is ready if (fds[0]. revents & POLLIN) { char buffer[1024]; int bytesRead = read(0, buffer, sizeof(buffer)); if (bytesRead > 0) { // process data read from standard input printf("Read %d bytes: %.*s\\ ", bytesRead, bytesRead, buffer); } else if (bytesRead == 0) { printf("EOF\\ "); break; } else { perror("read"); break; } } } } return 0; }
The implementation of poll is very similar to select, except that the way to describe the fd collection is different. poll uses the pollfd structure instead of the fd_set structure of select. poll solves the problem of the maximum number of file descriptors, but it also needs to copy all fd from user mode to Kernel mode also needs to linearly traverse all fd collections, so it is only a distinction in implementation details from select, and there is no essential difference.
epoll:
epoll API
#include <sys/epoll.h> int epoll_create(int size); // int epoll_create1(int flags); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
#include <stdio.h> #include <stdlib.h> #include <sys/epoll.h> int main() { int epoll_fd = epoll_create1(0); // create epoll instance if (epoll_fd == -1) { perror("epoll_create1"); return 1; } struct epoll_event event; event.events = EPOLLIN; // focus on readable events event.data.fd = 0; // standard input file descriptor is 0 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event) == -1) { perror("epoll_ctl"); close(epoll_fd); return 1; } struct epoll_event events[1]; int max_events = 1; int timeout = 5000; // set the timeout to 5 seconds while (1) { int ready = epoll_wait(epoll_fd, events, max_events, timeout); // call the epoll_wait function to wait for events to occur if (ready == -1) { perror("epoll_wait"); break; } else if (ready == 0) { printf("Timeout\\ "); } else { // check if standard input is ready if (events[0].events & EPOLLIN) { char buffer[1024]; int bytesRead = read(0, buffer, sizeof(buffer)); if (bytesRead > 0) { // process data read from standard input printf("Read %d bytes: %.*s\\ ", bytesRead, bytesRead, buffer); } else if (bytesRead == 0) { printf("EOF\\ "); break; } else { perror("read"); break; } } } } close(epoll_fd); return 0; }
Use code examples to better understand and distinguish the differences between them.
Where is the high performance of epoll compared to select/poll?
epoll
has several performance advantages over select
and poll
:
-
Event notification method:
select
andpoll
need to traverse the entire file descriptor collection to find ready descriptors when an event occurs, whileepoll
uses the callback mechanism to return only ready file descriptors, avoiding the overhead of traversing the entire collection. -
Higher scalability: The time complexity of
select
andpoll
is O(n), where n is the number of monitored file descriptors, As the number of file descriptors increases, the performance drops significantly. However,epoll
uses a Red-Black Tree to store file descriptors, with a time complexity of O(log n) and better scalability. -
More efficient kernel notification mechanism:
select
andpoll
need to check the state change of the file descriptor through polling or timer, andepoll
uses the kernel’s event notification mechanism (such as epoll_wait) to directly notify the application program when the file descriptor state changes, avoiding invalid polling and timer overhead. -
Support large-scale concurrency: Since
epoll
uses a red-black tree to store file descriptors, it can support large-scale concurrent connections. In contrast,select
andpoll
perform poorly for large collections of file descriptors.