select与epoll,多路IO复用
2011-09-24 22:04
471 查看
当一个节点建立了与多个节点之间的连接时,如何去高效的处理多个连接的数据呢?
可能有的连接有数据,有的连接没有数据,这中情况下,我们总不能一个个的去recv尝试吧? linux下面给我们提供了select和epoll两组接口以处理这种情况。
1 select:
#include <sys/select.h>
#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);
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使用时,有三个集合: readfds,writefds,exceptfds集合,每次select会将得到的有对应事件文件描述符放在这三个集合中。调用者要自行判断文件描述符(或者socket)是否在对应的集合中。每次判定前,都要清空要传入的集合。nfds是需要监管的描述符的最大值+1.比这个值更大的描述符上如果有事件发生不会被监管到。
2 poll:
#include <sys/epoll.h>
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
poll的接口是每次传递一个需要监听的集合,集合中的每个对象包含了要监听的文件描述符,要监听的事件,已经最终得到的事件。
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno。
3 epoll:
#include <sys/epoll.h>
int 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);
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
epoll的使用,首先通过epoll_create创建一个epoll对象,得到一个epoll的描述符,我们需要监听哪个fd上的事件通过epoll_ctl,最后 epoll_wait会返回发生了时间的数目,具体的时间在参数events中。
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events
.data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events
.data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock);
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events
.data.fd);
}
}
}
4 对比:
select使用时,FD_SET是一个静态变量,其最大值由FD_SETSIZE设置,对于每个进程默认2048,只能通过开启多个进程或者重新编译内核解决该问题。
poll所支持的fd是一个进程能够打开的文件描述符。
select和poll,每次都会线性的判断fd集合中哪些发生了事件,并将该事件记录下来。如果同时建立的连接很多,但是只有少数的有事件发生,这种情况下,效率就会很低。epoll的实现中就避免了该问题,对于要监管的每个fd都会有回调函数,当该fd上发生事件时,会调用对应的回调函数。这样,在连接很多,少数事件发生的情况下,依旧会效率很高。当然,如果连接很多,大部分连接都有事件时,两者的效率应该是差不多的。
可能有的连接有数据,有的连接没有数据,这中情况下,我们总不能一个个的去recv尝试吧? linux下面给我们提供了select和epoll两组接口以处理这种情况。
1 select:
#include <sys/select.h>
#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);
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使用时,有三个集合: readfds,writefds,exceptfds集合,每次select会将得到的有对应事件文件描述符放在这三个集合中。调用者要自行判断文件描述符(或者socket)是否在对应的集合中。每次判定前,都要清空要传入的集合。nfds是需要监管的描述符的最大值+1.比这个值更大的描述符上如果有事件发生不会被监管到。
2 poll:
#include <sys/epoll.h>
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
poll的接口是每次传递一个需要监听的集合,集合中的每个对象包含了要监听的文件描述符,要监听的事件,已经最终得到的事件。
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno。
3 epoll:
#include <sys/epoll.h>
int 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);
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
epoll的使用,首先通过epoll_create创建一个epoll对象,得到一个epoll的描述符,我们需要监听哪个fd上的事件通过epoll_ctl,最后 epoll_wait会返回发生了时间的数目,具体的时间在参数events中。
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events
.data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events
.data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock);
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events
.data.fd);
}
}
}
4 对比:
select使用时,FD_SET是一个静态变量,其最大值由FD_SETSIZE设置,对于每个进程默认2048,只能通过开启多个进程或者重新编译内核解决该问题。
poll所支持的fd是一个进程能够打开的文件描述符。
select和poll,每次都会线性的判断fd集合中哪些发生了事件,并将该事件记录下来。如果同时建立的连接很多,但是只有少数的有事件发生,这种情况下,效率就会很低。epoll的实现中就避免了该问题,对于要监管的每个fd都会有回调函数,当该fd上发生事件时,会调用对应的回调函数。这样,在连接很多,少数事件发生的情况下,依旧会效率很高。当然,如果连接很多,大部分连接都有事件时,两者的效率应该是差不多的。
相关文章推荐
- 多路IO复用模型 select epoll 等
- IO多路复用之select、poll、epoll详解
- Linux下多路复用IO接口 epoll select poll 的区别
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- 三种多路复用IO实现方式:select,poll,epoll的区别
- 多路IO复用模型 select epoll 等
- 聊聊IO多路复用之select、poll、epoll详解
- IO 多路复用之 select、poll、epoll 详解
- 服务器基础:IO多路复用之select、poll、epoll详解
- [nginx] Linux下多路复用IO接口 epoll select poll 的区别
- IO多路复用之select、poll、epoll详解
- IO多路复用--select、 poll、 epoll的区别
- 三种多路复用IO实现方式:select,poll,epoll的区别
- 聊聊IO多路复用之select、poll、epoll详解
- 基于表格形式的select,poll,epoll对比-IO多路复用函数的应用场景
- IO多路复用之select、poll以及epoll
- IO多路复用select,poll,epoll的区别
- 网络通信 --> IO多路复用之select、poll、epoll详解
- 多路复用IO机制:select,poll,epoll
- IO多路复用:select,poll,epoll的区别