#include#include #include #include int select(int maxfd,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
//pseudocode
#define n 1024
int fd[n];
fd_set rdset;
while (1)
{
FD_ZERO(&rdset);
for (i = 0; i < n; i++)
{
FD_SET(fd[i], &rdset);
}
select(fd[n - 1] + 1, &rdset, NULL, NULL, NULL);
for (i = 0; i < n; i++)
{
if (FD_ISSET(fd[i] &rdset))
{
//do something
}
}
}
缺点
1、只能支持1024个描述符
2、每次都要进行重复地在用户态和内核态之间进行描述符的拷贝。select调用前要用用户态拷贝描述符到内核态,select返回前需要用将描述符从内核态拷贝到用户态
3、需要遍历所有监听的描述符才能判断哪个描述符已经就绪
4、select的触发方式为水平触发(level),如果本次应用程序没有对该描述符进行处理,下次select调用返回后还是会将该描述符继续通知
#includeint poll(struct pollfd *fds, nfds_t nfds, int timeout);
//pseudocode
#define n 2048
int fd[n];
struct pollfd peerfd[n];
for (i = 0; i < n; i++)
{
peerfd[i].fd = fd[i];
peerfd[i].events = POLLIN;
}
poll(peerfd, n, -1);
for (i = 0; i < n; i++)
{
if (peerfd[i].revents & POLLIN)
{
//do something
}
}
缺点
- 除了没有select的1024个描述符的限制,其它的缺点都和select一样
- poll的实现是用链表保存描述符,而select使用数组保存描述符,所以poll没有描述符大小限制
#includeint epoll_create(int size); 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);
//pseudocode
#define n 4096
int fd[n];
struct epoll_event ev[n];
struct epoll_event events[n];
epollfd = epoll_create1(0);
for (i = 0; i < n; i++)
{
ev[i].events = EPOLLIN;
ev[i].data.fd = fd[i];
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd[i], &ev[i]);
}
nfds = epoll_wait(epollfd, events, n, -1);
for (i = 0; i < nfds; i++)
{
if (events[i].events & EPOLLIN)
{
//do something
}
}
优点
1、epoll只需要从用户态拷贝描述符一次到内核态
2、epoll_wait返回的是已经就绪的描述符,也就是从内核态拷贝到用户态的描述符不会很多,而且不用像select或poll那样遍历所有描述符



