Select 、Poll 和 Epoll
作用
Epoll 和 Select 的作用都是为了多I/O同步复用的问题,利用Epoll、Poll或Select函数指定内核监听多个I/O的读、写、异常事件,避免每一个I/O都指定一个处理线程,导致开销过大。
Select
需要指定最大监听描述符的值,还要传入对应监听事件的fd_set(read\write\exception),每一次Select函数触发都会将集合内容修改,所以开发者要设置缓存区来记录所有文件描述符,每次调用函数的时候都要重新初始化监听集合。
在Select触发后开发者需要去将缓存区的文件描述符与监听集合去匹配,例如描述符100在read set中,说明100有数据写入,可以立刻去读取,然后丢给worker线程去处理:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <wait.h>
#include <signal.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#define MAXBUF 256
void child_process(void)
{
sleep(2);
char msg[MAXBUF];
struct sockaddr_in addr = {0};
int n, sockfd,num=1;
srandom(getpid());
/* Create socket and connect to server */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
printf("child {%d} connected \n", getpid());
while(1){
int sl = (random() % 10 ) + 1;
num++;
sleep(sl);
sprintf (msg, "Test message %d from client %d", num, getpid());
n = write(sockfd, msg, strlen(msg)); /* Send message */
}
}
int main()
{
char buffer[MAXBUF];
int fds[5];
struct sockaddr_in addr;
struct sockaddr_in client;
int addrlen, n,i,max=0;;
int sockfd, commfd;
fd_set rset;
for(i=0;i<5;i++)
{
if(fork() == 0)
{
child_process();
exit(0);
}
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd,(struct sockaddr*)&addr ,sizeof(addr));
listen (sockfd, 5);
for (i=0;i<5;i++)
{
memset(&client, 0, sizeof (client));
addrlen = sizeof(client);
fds[i] = accept(sockfd,(struct sockaddr*)&client, &addrlen);
if(fds[i] > max)
max = fds[i];
}
while(1){
FD_ZERO(&rset);
for (i = 0; i< 5; i++ ) {
FD_SET(fds[i],&rset);
}
puts("round again");
select(max+1, &rset, NULL, NULL, NULL);
for(i=0;i<5;i++) {
if (FD_ISSET(fds[i], &rset)){
memset(buffer,0,MAXBUF);
read(fds[i], buffer, MAXBUF);
puts(buffer);
}
}
}
return 0;
}
Select 要求开发者去统计最大监听范围,fd_set是一个长度为32的整数数组,每一个fd对应一个bit位。内核会遍历传入集合的每一bit到最大描述符对应的bit。同时用户进程在处理返回结果的时候仍然要遍历集合,在用户进程中处理事件的效率是O(n)。它主要的优点是跨平台比较方便windows和linux都支持该函数。
同时需要注意的是,如果正被select监听的文件描述符在另一个线程被关闭,实际上这个文件描述符不会被真正的关闭。
Select 在处理超时精度上要比Epoll和Poll高,Epoll和Poll只能处理一毫秒的精度[4]。
Poll
在使用的时候要去传入一个pollfd数组,其中包含每一个需要监听的文件描述符。pollfd中包含监听事件和返回事件,不需要每次都去构建。
struct pollfd {
int fd;
short events;
short revents;
};
与Select类似,用户在处理返回结果的时候要轮寻检查revents,看看是否和期待的事件一致。在内核中也需要遍历文件描述符的列表来找到受监视的对象,然后再遍历文件描述符列表去设置事件。它也具备Select的缺陷,即无法在其他线程中关闭正在监听的文件描述符[4]:
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
for (i=0;i<5;i++)
{
memset(&client, 0, sizeof (client));
addrlen = sizeof(client);
pollfds[i].fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);
pollfds[i].events = POLLIN;
}
sleep(1);
while(1){
puts("round again");
poll(pollfds, 5, 50000);
for(i=0;i<5;i++) {
if (pollfds[i].revents & POLLIN){
pollfds[i].revents = 0;
memset(buffer,0,MAXBUF);
read(pollfds[i].fd, buffer, MAXBUF);
puts(buffer);
}
}
}
Poll 与 Select相比:
- 不需要统计最大监听范围
- 更适合大值的文件描述符监听
- 文件描述符监听集合不用每次都重建,但仍需要自己维护
- Select移植性更好,超时监听更加灵敏
Epoll
epoll 会在内核中帮我们创建一个context用于记录要监听的文件描述符,可以在等待I/O事件的同时去添加或移除文件描述符。每次只会返回就绪的文件描述符,用户进程在处理返回结果上更加方便,时间复杂度为O(1)。但由于Epoll只在linux中支持,不支持跨平台,也是最晚出现的方法。
struct epoll_event events[5];
int epfd = epoll_create(10); //指定监听数量,只是建议内核初始化分配空间,并不影响监听数量上限。
...
...
for (i=0;i<5;i++)
{
static struct epoll_event ev;
memset(&client, 0, sizeof (client));
addrlen = sizeof(client);
ev.data.fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev);
}
while(1){
puts("round again");
nfds = epoll_wait(epfd, events, 5, 10000);
for(i=0;i<nfds;i++) {
memset(buffer,0,MAXBUF);
read(events[i].data.fd, buffer, MAXBUF);
puts(buffer);
}
}
因为监听的描述符记录在了内核中,所以即使用户进程被关闭了,内核也会去监视对应的对象,可以用于边缘触发等场景。
Epoll vs Select
- 可以在等待的时候添加或移除监听对象
- 只返回准备就绪的文件描述符
- 用户进程处理事件效率更高(Epoll是O(1),Select是O(n))
- Epoll只在Linux中支持,移植性差
- Select超时监听更加灵敏
- Epoll使用起来更复杂
Epoll vs Poll
- 可以在等待的时候添加或移除监听对象
- 只返回准备就绪的文件描述符
- Epoll使用起来更复杂
- 用户进程处理事件效率更高(Epoll是O(1),Poll是O(n))
- Epoll使用起来更复杂
总结
Select、Epoll、Poll都是I/O多路复用的一种技术,都属于同步I/O的范畴,用户进程同时监听多个I/O事件[2]。
Select与Poll比较类似,每次调用函数都需要将监听的集合传入内核中,即要copy一次,返回的结果要轮寻匹配。不过相较Select而言Poll没有最大监听范围限制(Select在linux中最大监听范围为1024,此值可以修改),在没有变动的情况下无需每次调用时重新构建监听集合,而Select在跨平台性上和处理超时精度上是三者中最优的[4]。
Epoll是Select和Poll的优化版,它采用一个内核context来维护监听集合,如果在不需要变动的情况下,监听描述符设置只需要从用户进程向内核中copy一次。但读者要注意,每调用epoll_ctl函数去更新监听描述符的时候都是一次copy,由于不支持批量操作,每一个描述符都需要执行一次copy,所以如果是频繁的短链接,epoll的效率未必比poll好[4]。每次直接返回准备态的文件描述符集合,不需要轮寻匹配,用户进程处理事件的效率是最高的。
参考地址
- [1] http://devarea.com/linux-io-multiplexing-select-vs-poll-vs-epoll/#.W5Bc7dIzbDc
- [2] https://segmentfault.com/a/1190000003063859
- [3] https://stackoverflow.com/questions/17355593/why-is-epoll-faster-than-select
- [4] https://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/
Select 、Poll 和 Epoll的更多相关文章
- Linux下select, poll和epoll IO模型的详解
http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...
- I/O复用中的 select poll 和 epoll
I/O复用中的 select poll 和 epoll: 这里有一些不错的资料: I/O多路复用技术之select模型: http://blog.csdn.net/nk_test/article/de ...
- (转)Linux下select, poll和epoll IO模型的详解
Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...
- linux select poll and epoll
这里以socket文件来阐述它们之间的区别,假设现在服务器端有100 000个连接,即已经创建了100 000个socket. 1 select和poll 在我们的线程中,我们会弄一个死循环,在循环里 ...
- Select,poll,epoll复用
Select,poll,epoll复用 1)select模块以列表的形式接受四个参数,分别是可读对象,可写对象,产生异常的对象,和超时设置.当监控符对象发生变化时,select会返回发生变化的对象列表 ...
- 聊聊select, poll 和 epoll
聊聊select, poll 和 epoll 假设项目上需要实现一个TCP的客户端和服务器从而进行跨机器的数据收发,我们很可能翻阅一些资料,然后写出如下的代码. 服务端 void func(int s ...
- [转载] select, poll和epoll的区别
源地址:http://sheepxxyz.blog.163.com/blog/static/61116213201022003513530/ 随着2.6内核对epoll的完全支持,网络上很多的文章和示 ...
- Linux中select poll和epoll的区别
在Linux Socket服务器短编程时,为了处理大量客户的连接请求,需要使用非阻塞I/O和复用,select.poll和epoll是Linux API提供的I/O复用方式,自从Linux 2.6中加 ...
- Linux select/poll和epoll实现机制对比
关于这个话题,网上已经介绍的比较多,这里只是以流程图形式做一个简单明了的对比,方便区分. 一.select/poll实现机制 特点: 1.select/poll每次都需要重复传递全部的监听fd进来,涉 ...
- select,poll 和 epoll ??
其实所有的 I/O 都是轮询的方法,只不过实现的层面不同罢了. 其中 tornado 使用的就是 epoll 的. selec,poll 和 epoll 区别总结 基本上 select 有 3 个缺点 ...
随机推荐
- cocos2d-x3.0 用CCDictionary写文件
bool CDownLoad_LocalData::WriteToConfigFile( DownLoadLocalData* downdata ){ CCDictionary* pDict = CC ...
- CCF认证201809-2买菜
问题描述 小H和小W来到了一条街上,两人分开买菜,他们买菜的过程可以描述为,去店里买一些菜然后去旁边的一个广场把菜装上车,两人都要买n种菜,所以也都要装n次车.具体的,对于小H来说有n个不相交的时间段 ...
- 【Javascript-基础-Date】本地时间与UTC(GMT)时间转换
UTC(GMT) 整个地球分为二十四时区,每个时区都有自己的本地时间.在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC, Universal Time Coordinat ...
- oracle与infomix异同点
之前是做oracle数据库应用开发的,现在工作用的是informix,特别不习惯.用了一段时间后才慢慢适应,最近做系统升级,把informix换成oracle数据库.顺便整理了一下informix与o ...
- Oracle高级函数篇之递归查询start with connect by prior简单用法
路飞:" 把原来CSDN的博客转移到博客园咯!" 前段时间,自己负责的任务中刚好涉及到了组织关系的业务需求,自己用了oracle递归查询.下面简单来举个例子.在工作中我们经常会遇到 ...
- koa2学习笔记03 - 给koa2配置session ——koa2结构分层、配置数据库、接口
前言 这一章写的很没有底气,因为我完全不懂一个正经的后台应用是怎么结构分层的, 所有只能按照我自己的理解去写,即使这样也仅仅只分离出了controller层, 至于所谓的service层,dao层,完 ...
- P1015 回文数解题思路(非原创)
测试 #include<bits/stdc++.h> using namespace std; int n,m,step; string nn; int len,nex; bool dfs ...
- iview-cli 项目、iView admin 代理与跨域问题解决方案
iview-cli 项目.iView admin 跨域.代理问题解决方案 在webpack.dev.config.js文件中: 添加: devServer: { historyApiFallback: ...
- vue-cli3 创建选项选择
1.创建新项目: vue create hello-world 2.选择配置 3.自定义选择配置,需要什么就选什么 4. 是否使用带历史纪录的路由,这里一般是Y 5.预编译器选择什么 6.eslint ...
- Rabbitmq(三)
1.在服务器安装好rabbitmq后,自己配置自己用的vhost,exchange和queue的绑定 2.项目添加RabbitMqClient.dll(nuget获取)引用 3.添加helper就可以 ...