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 个缺点 ...
随机推荐
- ES6中let与const命令详解
阮一峰ES6入门 let 作用域 let命令用来声明变量,但声明的变量只在let命令所在的代码块内有效. { let a = 10; var b = 1; } a // ReferenceError: ...
- select 宽度跟随option内容自适应
传统的select在没有设置固定宽度的情况,会因为自身的 option 选项的里,宽度最宽的option作为select本身的宽度 例如 可见效果为: select的宽度因为"宽度最宽的op ...
- 初次使用vue-cli3 来搭建项目
1,细数项目中使用的技术:vue, vue-router, vuex ,axios,vue-cli3, 快速建站. 2,mock技术使用的express-mockjs . 由于cli3 最新版的话缺少 ...
- Redis之Redis事务
Redis事务的概念: Redis 事务的本质是一组命令的集合.事务支持一次执行多个命令,一个事务中所有命令都会被序列化.在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会 ...
- laychat聊天功能
windows版本:1.直接下载laychat聊天室压缩包,并解压到PHPstudy本地PHP环境中去:2.进入E:\PHPTutorial\WWW\laychat-master\vendor\Wor ...
- vue-nuxt.js部署到宝塔主机服务器
废话不多说,直接上步骤,如下: 本文章为在 vue环境下使用了nuxt.js 1.搭建环境--由于本人安装的是宝塔主机,因此如下: 由于我直接使用的是宝塔主机,直接去“软件管理”安装 PM2管理器. ...
- 动态链接库 —— Dll 基础
1. DLL 的初识 在 windows 中,动态链接库是不可缺少的一部分,windows 应用程序程序接口提供的所有函数都包含在 DLL 中,其中有三个非常重要的系统 DLL 文件,分别为 Kern ...
- hadoop生态搭建(3节点)
软件:CentOS-7 VMware12 SSHSecureShellClient shell工具:Xshell 规划 vm网络配置 01.基础配置 02.ssh配置 03.zookeep ...
- [Golang学习笔记] 08 链表
链表(Linked list)是一种常见数据结构,但并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针. 由于不必须按顺序存储,链表在插入的时候可以达到O(1),比顺序表快得多,但是查 ...
- BFC与浮动
一.BFC的含义 BFC(block formatting contexts) 块级元素格式化上下文,它决定了块级元素如何对它的内容进行布局,以及与其它元素的关系和相互作用. 块级元素:父级(是一个块 ...