epollselect和epoll的区别
epoll 时间:2021-07-12 阅读:(
)
epoll和select的区别
epoll跟select都能提供多路I/O复用的解决方案。
在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现。
网上现在关于这两者不同的介绍已经到处都是了。
我这里也不能多说出什么东西,只是记录下我看了实现代码之后的一些总结。
两者的使用场景一般是通过一个入口能够同时监控多路I/O。
一般使用的接口,
epool就是
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
select为:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
通过上述两个函数能够将调用线程阻塞,线程变为可执行条件有两种情况:
无任何事件发生,超时时间已过
在所控制的I/O有事件到来
epoll_wait函数中看不到相关的监控信息,因为是通过epoll_ctl已经加入,而select之间在函数调用中由(fd_set *readfds, fd_set *writefds, fd_set *exceptfds)传入。
epoll_wait饭互结果通过events返回,而select的传入参数也是传出参数。
两者传出参数均表示发生事件的对应I/O标识。
两种方式的区别主要体现在以下几个方面:
select所能控制的I/O数有限,这主要是因为fd_set数据结构是一个有大小的,相当与一个定长所数组。
select每次都需要重新设置所要监控的fd_set(因为调用之后会改变其内容),这增加了程序开销。
select的性能要比epoll差,具体原因会在后续内容中详细说明。
嗯,说道这个为什么select要差,那就要从这个select API说起了。
这个传进去一个数组,内部实现也不知道那个有哪个没有,所以要遍历一遍。
假设说我只监控一个文件描述符,但是他是1000。
那么select需要遍历前999个之后再来poll这个1000的文件描述符,而epoll则不需要,因为在之前epoll_ctl的调用过程中,已经维护了一个队列,所以直接等待事件到来就可以了。
Linux中select此段相关代码为:
/* 遍历所有传入的fd_set */
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
const struct file_operations *f_op = NULL;
struct file *file = NULL;
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
/* 此处跳无需监控的fd, 白白的浪费时间啊…… */
if (all_bits == 0) {
i += __NFDBITS;
continue;
}
/* 后续进行一些相关操作 */
}
而epoll则无需进行此类操作,直接检测内部维护的一个就绪队列,如果队列有内容,说明有I/O就绪,那么直接赋值返回内容,成功返回,如果没有成功,那么睡眠,等待就绪队列非空。
通过这个两者的比较,其实两者的差距啊,大部分是因为这个API设计所决定的,select就设计成这样一个API,内部再怎么优化也只能是这么个烂样子,而epoll这样维护与等待分离,灵活多变,最后也就带来了相对的高性能,以及可扩展性。
select poll和epoll的区别
主要是epoll_create,epoll_ctl和epoll_wait三个函数。
epoll_create函数创建epoll文件描述符,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。
返回是epoll描述符。
-1表示创建失败。
epoll_ctl 控制对指定描述符fd执行op操作,event是与fd关联的监听事件。
op操作有三种:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。
分别添加、删除和修改对fd的监听事件。
epoll_wait 等待epfd上的io事件,最多返回maxevents个事件。
在
select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一
个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()
时便得到通知。
Linux中select poll和epoll的区别
select、poll、epoll_wait参数及实现对比
1. select的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),位数组的每一位代表其对应的描述符是否需要被检查。
select的第二三四个参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件。
所以每次调用select前都需要重新初始化fdset。
timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。
select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返回。
select返回后,需要逐一检查关注的描述符是否被SET(事件是否发生)。
2. poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。
poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。
poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。
3. epoll通过epoll_create创建一个用于epoll轮询的描述符,通过epoll_ctl添加/修改/删除事件,通过epoll_wait检查事件,epoll_wait的第二个参数用于存放结果。
epoll与select、poll不同,首先,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的epoll描述符关联起来。
另外epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。
epoll返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像poll、select那样进行轮询检查。
select、poll、epoll_wait性能对比
select、poll的内部实现机制相似,性能差别主要在于向内核传递参数以及对fdset的位操作上,另外,select存在描述符数的硬限制,不能处理很大的描述符集合。
这里主要考察poll与epoll在不同大小描述符集合的情况下性能的差异。
测试程序会统计在不同的文件描述符集合的情况下,1s内poll与epoll调用的次数。
统计结果如下,从结果可以看出,对poll而言,每秒钟内的系统调用数目虽集合增大而很快降低,而epoll基本保持不变,具有很好的扩展性。
IO模型及select,poll,epoll和kqueue的区别
(一)首先,介绍几种常见的I/O模型及其区别,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知。
阻塞与否,取决于实现IO交换的方式。
异步阻塞是基于select,select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄.
异步非阻塞直接在完成后通知,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
1 blocking I/O
这个不用多解释吧,阻塞套接字。
下图是它调用过程的图示:
重点解释下上图,下面例子都会讲到。
首先application调用 recvfrom()转入kernel,注意kernel有2个过程,wait for data和copy data from kernel to user。
直到最后plete后,recvfrom()才返回。
此过程一直是阻塞的。
2 nonblocking I/O:
与blocking I/O对立的,非阻塞套接字,调用过程图如下:
可以看见,如果直接操作它,那就是个轮询。
。
直到内核缓冲区有数据。
3 I/O multiplexing (select and poll)
最常见的I/O复用模型,select。
select先阻塞,有活动套接字才返回。
与blocking I/O相比,select会有两次系统调用,但是select能处理多个套接字。
4 signal driven I/O (SIGIO)
只有UNIX系统支持,感兴趣的课查阅相关资料
与I/O multiplexing (select and poll)相比,它的优势是,免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。
5 asynchronous I/O (the POSIX aio_functions)
很少有*nix系统支持,windows的IOCP则是此模型
完全异步的I/O复用机制,因为纵观上面其它四种模型,至少都会在由kernel copy data to appliction时阻塞。
而该模型是当copy完成后才通知application,可见是纯异步的。
好像只有windows的完成端口是这个模型,效率也很出色。
6 下面是以上五种模型的比较
可以看出,越往后,阻塞越少,理论上效率也是最优。
=====================分割线==================================
5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue按号入座那就OK了。
select和iocp分别对应第3种与第5种模型,那么epoll与kqueue呢?其实也于select属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback机制。
为什么epoll,kqueue比select高级?
答案是,他们无轮询。
因为他们用callback取代了。
想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。
这会浪费很多CPU时间。
如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
windows or *nix (IOCP or kqueue/epoll)?
诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。
而且正如上面所述,kqueue/epoll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。
但是,这层小小的阻塞无足轻重,kqueue与epoll已经做得很优秀了。
提供一致的接口,IO Design Patterns
实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE,Libevent(基于reactor模式)这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。
说到这里又得说说2个设计模式,Reactor and Proactor。
见:Reactor模式--VS--Proactor模式。
Libevent是Reactor模型,ACE提供Proactor模型。
实际都是对各种I/O复用机制的封装。
Java nio包是什么I/O机制?
现在可以确定,目前的java本质是select()模型,可以检查/jre/bin/nio.dll得知。
至于java服务器为什么效率还不错。
。
我也不得而知,可能是设计得比较好吧。
。
-_-。
=====================分割线==================================
总结一些重点:
只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。
select低效是因为每次它都需要轮询。
但低效也是相对的,视情况而定,也可通过良好的设计改善
epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。
java nio包是select模型。
。
(二)epoll 与select的区别
1. 使用多进程或者多线程,但是这种方法会造成程序的复杂,而且对与进程与线程的创建维护也需要很多的开销。
(Apache服务器是用的子进程的方式,优点可以隔离用户) (同步阻塞IO)
2.一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),然后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。
select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。
区别(epoll相对select优点)主要有三:
1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd。
而epoll没有,它的限制是最大的打开文件句柄数目。
2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。
epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。
那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。
但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。
3.使用mmap加速内核与用户空间的消息传递。
无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
关于epoll工作模式ET,LT
epoll有两种工作方式
ET:Edge Triggered,边缘触发。
仅当状态发生变化时才会通知,epoll_wait返回。
换句话,就是对于一个事件,只通知一次。
且只支持非阻塞的socket。
LT:Level Triggered,电平触发(默认工作方式)。
类似select/poll,只要还有没有处理的事件就会一直通知,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll.支持阻塞和不阻塞的socket。
三 Linux并发网络编程模型
1 Apache 模型,简称 PPC ( Process Per Connection ,):为每个连接分配一个进程。
主机分配给每个连接的时间和空间上代价较大,并且随着连接的增多,大量进程间切换开销也增长了。
很难应对大量的客户并发连接。
2 TPC 模型( Thread Per Connection ):每个连接一个线程。
和PCC类似。
3 select 模型:I/O多路复用技术。
.1 每个连接对应一个描述。
select模型受限于 FD_SETSIZE即进程最大打开的描述符数linux2.6.35为1024,实际上linux每个进程所能打开描数字的个数仅受限于内存大小,然而在设计select的系统调用时,却是参考FD_SETSIZE的值。
可通过重新编译内核更改此值,但不能根治此问题,对于百万级的用户连接请求 即便增加相应 进程数, 仍显得杯水车薪呀。
.2select每次都会扫描一个文件描述符的集合,这个集合的大小是作为select第一个参数传入的值。
但是每个进程所能打开文件描述符若是增加了 ,扫描的效率也将减小。
.3内核到用户空间,采用内存复制传递文件描述上发生的信息。
4 poll 模型:I/O多路复用技术。
poll模型将不会受限于FD_SETSIZE,因为内核所扫描的文件 描述符集合的大小是由用户指定的,即poll的第二个参数。
但仍有扫描效率和内存拷贝问题。
5 pselect模型:I/O多路复用技术。
同select。
6 epoll模型:
.1)无文件描述字大小限制仅与内存大小相关
.2)epoll返回时已经明确的知道哪个socket fd发生了什么事件,不用像select那样再一个个比对。
.3)内核到用户空间采用共享内存方式,传递消息。
四 :FAQ
1、单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。
所以你有还是必要建立线程池来发挥更大的效能。
2、如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。
3、如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。
4、如果多个事件同时触发epoll,则多个事件会被联合在一起返回。
5、epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。
6、为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。
linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。
[原]浅谈几种服务器端模型——反应堆模式(epoll 简介)
各有各的好处吧,今天来讲讲关于非阻塞的异步IO。
说到异步IO,其实现在很难实现真正的异步,大部分情况下仍然需要阻塞在某个多路复用函数,比如select 或者 epoll 上,得到就绪描述符,然后调用注册在相应描述符上的回调函数。
这种方式是现在的反应堆设计的基本思路。
我截取一段反应堆模型的图给大家看看。
这个图是截取至 python的 twisted 服务器的反应堆文章介绍,但是大致和我们需要的理念一样。
事件循环阻塞查看描述符是否就绪,当就绪后返回可读或可写的描述符,也有可能带外数据或者出错等情况。
因为select 很多文章都介绍了,下面我就以 epoll 为例,貌似是2.4.6还是哪个版本以后加入的IO多路复用方式。
epoll 较select 的一些优点就不多说了,内核采用红黑树机制,大大提高了epoll 的性能。
著名的 libevent Nginx等内部都采用这个机制。
废话不多说,看一个简单的epoll 模式,其实本来不想介绍这个的,因为直接 man epoll 就可以看到一个简单的demo,但是为了文章的连贯性,还是继续把这部分介绍一下。
epoll 主要有几个函数:int epoll_create(int size);在现在的Linux版本中,size 已不重要,默认的不超过最大值就可以。
size 就是描述符数目的最大值。
函数的返回值是一个描述符(句柄),很简单的就创建了epoll.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);第一个参数是由 epoll_create 返回的描述符第二个参数是由宏定义的几个值EPOLL_CTL_ADD:类似于 select 的 FD_SET() ,将一个描述符加入到epoll 监听队列中EPOLL_CTL_MOD:修改已经注册的fd的事件类型EPOLL_CTL_DEL:将一个描述符从epoll 监听队列中删除第三个参数是需要加入的描述符第四个是一个结构体参数,结构是这样的struct epoll_event { __uint32_t events; epoll_data_t data; }; typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; epoll_event 结构体里面的events 表示的是返回的事件类型或者是加入时候的事件类型。
也有可能是带外数据或者错误等,它由几个宏定义:EPOLLIN :文件描述符上的读事件EPOLLOUT:文件描述符上的写事件EPOLLPRI:描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR:描述符发生错误;EPOLLHUP:描述符被挂断;EPOLLET: 边缘触发(Edge Triggered)模式EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里 值得一说的是,很多文章都没有提到这个宏其实可能是由你自己改变的,通过 epoll_ctl 或者是在 epoll_wait 返回的时候操作系统改的,因为描述符有可能出错等。
一般情况下,对于一个描述符,可以使用 | 运算来组合。
添加一个描述符,监听是否可读或可写。
EPOLLIN | EPOLLOUT 注意一下epoll_data_t中的 ptr 或者 fd 而不是 ptr和fd,这个结构只能包含其中一个,所以在注册相应的描述符上的事件的时候,要么注册的是对应的描述符fd,要么注册的是相应的事件封装,当然,事件封装里面必然有fd,不然无法继续下面的操作。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);第一个参数是epoll的描述符,第二个参数是一个指向 struct epoll_event 的指针,这里需要传入的是一个数组,epoll_event 类型.第三个,最大的监听事件数组值。
第四个是超时时间,对于Nginx或者很多如libevent 的超时时间管理是利用红黑树和最小堆来管理的,很巧妙的方式,以后写一篇博文介绍,这里只需要知道timeout 是 epoll_wait 的阻塞的最大值,如果超过这个值不管是否有事件都返回,0表示立即返回,即有无事件都返回,-1 是永久阻塞。
一个简单的 epoll demo struct epoll_event ev,events[1024]; epfd=epoll_create(1024); for( ; ; ) { nfds = epoll_wait(epfd,events,1024,time_value); for(i=0;i<nfds;++i) { if(events[i].data.fd==listenfd) /*如果加入的监听描述符有事件*/ { connfd = ept(listenfd,(sockaddr *)&clientaddr, &clilen); /*ept这个连接并得到链接描述符,将描述符加入到epoll 监听事件队列*/
setnonblocking(connfd); ev.data.fd=connfd; ev.events=EPOLLIN|EPOLLET; /*读事件*/ epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); /*将新的fd添加到epoll的监听队列中*/ } else if( events[i].events&EPOLLIN ) //接收到数据,读socket { n = read(sockfd, line, MAXLINE)) < 0 ev.data.ptr = my_ev; //ev 可以是自己定义的事件封装或者是fd ev.events=EPOLLOUT|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);/*修改标识符,等待下一个循环时发送数据*/ } else if(events[i].events&EPOLLOUT) /*对应的描述符可写,即套接口缓冲区有缓冲区可写*/ { struct my_event* my_ev= (my_event*)events[i].data.ptr; sockfd = my_ev->fd; send( sockfd, ev->ptr, strlen((char*)my_ev->ptr), 0 ); ev.data.fd=sockfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } else { // } } } epoll 还没讲清楚,其中还有很多需要注意的地方。
只是想让不懂异步事件和反应堆模式的读者了解这种模式。
注意的是这种模式下连接描述符需要设置为非阻塞,然后IO 操作函数应该记录每次读写的状态,如果缓冲区满的话需要记录状态,下次返回这个描述符的时候继续上一次的状态继续传输或读取,因为一个套接口缓冲区读取的是应用层数据,而TCP层的数据如果比较大的时候分段的话会导致一次不能完全读取或写入全部数据而套接口缓冲区已经满了。
需要选取的模式是LT 水平触发方式,如果是ET 边缘触发方式,一次读取套接口或者写入套接口但是缓冲区满了不能继续写后,epoll_wait不会继续返回,不需要状态机记录。
ET 方式也是所谓的高速模式。
总结:这里只是对epoll 做了一个简单的介绍,如有错误,请指教。
希望大牛们不要介意,承前启后,后面会有一个反应堆的框架的介绍,这里没有使用到事件封装和设置回调函数等,只是一个demo,还不是我自己写的。
今天就到这里吧select和epoll的区别
下面具体分析两者的区别。
1. select函数
函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数介绍:(1)nfds -- fdset集合中最大描述符值加1
(2)fdset -- 一个位数组,其大小限制为_FD_SETSIZE(1024)
位数组的每一位代表的是其对应的描述符是否需要被检查。
(3)readfds -- 读事件文件描述符数组
(4 )writefds -- 写事件文件描述符数组
(5)exceptfds -- 错误事件文件描述符数组
(6)timeout -- 超时事件,该结构被内核修改,其值为超时剩余时间。
对应内核:select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描 述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返
2. select/poll特点
传统的select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。
poll的执行分三部分:
(1).将用户传入的pollfd数组拷贝到内核空间,因为拷贝操作和数组长度相关,时间上这是一个O(n)操作
(2).查询每个文件描述符对应设备的状态,如果该设备尚未就绪,则在该设备的等待队列中加入一项并继续查询下一设备的状态。
查询完所有设备后如果没有一个设备就绪,这时则需要挂起当前进程等待,直到设备就绪或者超时。
设备就绪后进程被通知继续运行,这时再次遍历所有设备,以查找就绪设备。
这一步因为两次遍历所有设备,时间复杂度也是O(n),这里面不包括等待时间......
(3). 将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作,向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O(n)。
3. epoll机制
Linux 2.6内核完全支持epoll。
epoll的IO效率不随FD数目增加而线性下降。
要使用epoll只需要这三个系统调用:epoll_create(2), epoll_ctl(2), epoll_wait(2)
epoll用到的所有函数都是在头文件sys/epoll.h中声明的,内核实现中epoll是根据每个fd上面的callback函数实现的。
只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会。
如果所有的socket基本上都是活跃的---比如一个高速LAN环境,过多使用epoll,效率相比还有稍微的下降。
但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。
3.1 所用到的函数:
(1)、int epoll_create(int size)
该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围
(2)、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
如果调用成功返回0,不成功返回-1
int epoll_ctl{
int epfd,//由 epoll_create 生成的epoll专用的文件描述符
int op, //要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、
//EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除
int fd, //关联的文件描述符
struct epoll_event *event//指向epoll_event的指针
}
(3)、int epoll_wait(int
epfd, struct epoll_event *
events,int maxevents, int
timeout)
用于轮询I/O事件的发生,返回发生事件数
int epoll_wait{
int epfd,//由epoll_create 生成的epoll专用的文件描述符
struct epoll_event * events,//用于回传代处理事件的数组
int maxevents,//每次能处理的事件数
int timeout//等待I/O事件发生的超时值
//为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件
//为任意正整数的时候表示等这么长的时间,如果一直没有事件
//一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率
//如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率
}
epoll是为处理大批量句柄而作了改进的poll。
4. epoll的优点:
<1>支持一个进程打开大数目的socket描述符(FD)
select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。
对于那些需要支持的上万连接数目的IM服务器来说显然太少了。
这时候可以:
(1) 可以修改这个宏然后重新编译内核,不过资料也同时指出,这样也会带来网络效率的下降
(2) 可以选择多进程的解决方案,不过虽然linux上创建进程的代价比较下,但是仍旧是不可忽视的,所以也不是很完美的方案
epoll没有这样的限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,具体数组可以查看cat /proc/sys/fs/file-max查看,这个数目和系统内存关系很大。
<2>IO效率不随FD数目增加而线性下降
传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"活跃"的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。
epoll不存在这个问题,它只会对“活跃”的socket进行操作。
这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。
那么,只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,因为这时候推动力在os内核。
在一些 benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相 反,如果过多使用epoll_ctl,效率相比还有稍微的下降。
但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。
<3>使用mmap加速内核与用户空间的消息传递这点实际上涉及到epoll的具体实现了。
无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就 很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工 mmap这一步的。
<4>内核微调
这一点其实不算epoll的优点了,而是整个linux平台的优点。
也许你可以怀 疑linux平台,但是你无法回避linux平台赋予你微调内核的能力。
比如,内核TCP/IP协议栈使用内存池管理sk_buff结构,那么可以在运行 时期动态调整这个内存pool(skb_head_pool)的大小--- 通过echo XXXX>/proc//core/hot_list_length完成。
再比如listen函数的第2个参数(TCP完成3次握手 的数据包队列长度),也可以根据你平台内存大小动态调整。
更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网 卡驱动架构。
之前几个月由于CHIA挖矿导致全球固态硬盘的价格疯涨,如今硬盘挖矿基本上已死,硬盘的价格基本上恢复到常规价位,所以,pacificrack决定对全系Cloud server进行价格调整,降幅较大,“如果您是老用户,请通过续费管理或升级套餐,获取同步到最新的定价”。官方网站:https://pacificrack.com支持PayPal、支付宝等方式付款VPS特征:基于KVM虚拟,纯SSD raid...
AlphaVPS是一家保加利亚本土主机商(DA International Group Ltd),提供VPS主机及独立服务器租用等,数据中心包括美国(洛杉矶/纽约)、德国、英国和保加利亚等,公司办公地点跟他们提供的保加利亚数据中心在一栋楼内,自有硬件,提供IPv4+IPv6,支持PayPal或者信用卡等方式付款。商家提供的大硬盘VPS主机,提供128GB-2TB磁盘,最低年付15欧元起,也可以选择...
Moack怎么样?Moack(蘑菇主机)是一家成立于2016年的商家,据说是国人和韩国合资开办的主机商家,目前主要销售独立服务器,机房位于韩国MOACK机房,网络接入了kt/lg/kinx三条线路,目前到中国大陆的速度非常好,国内Ping值平均在45MS左右,而且商家的套餐比较便宜,针对国人有很多活动。不过目前如果购买机器如需现场处理,由于COVID-19越来越严重,MOACK办公楼里的人也被感染...
epoll为你推荐
flash控件手机怎么安装flash插件开票系统防伪税控开票系统怎么安装?洗牌算法c语言编程用扑克牌洗牌和发牌oncontextmenuoncontextmenu="return false"是什么意思李昊天铠甲勇士刑天中人物资料editplus破解版DBTools Manager Professional 破解版在哪里可以下载?spawningvc出现error spawning c1.exe怎么解决?系统论坛安卓系统论坛哪个好?medias请帮我详细解释一下 chronological order和in medias res新手怎么制作表格如何学会制作表格?
购买域名和空间 duniu 外国服务器 私服服务器 omnis 174.127.195.202 搜狗12306抢票助手 服务器是干什么的 360云服务 789 美国盐湖城 韩国代理ip 新加坡空间 万网注册 稳定空间 服务器硬件配置 apnic 蓝队云 winserver2008下载 留言板 更多