epollLinux中select poll和epoll的区别
epoll 时间:2021-07-12 阅读:(
)
linux下epoll如何实现高效处理
使用起来很清晰,首先要调用epoll_create建立一个epoll对象。
参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。
epoll_ctl可以操作上面建立的epoll,例如,将刚建立的socket加入到epoll中让其监控,或者把 epoll正在监控的某个socket句柄移出epoll,不再监控它等等。
epoll_wait在调用时,在给定的timeout时间内,当在监控的所有句柄中有事件发生时,就返回用户态的进程。
从上面的调用方式就可以看到epoll比select/poll的优越之处:因为后者每次调用时都要传递你所要监控的所有socket给select/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。
而我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。
所以,实际上在你调用epoll_create后,内核就已经在内核态开始准备帮你存储要监控的句柄了,每次调用epoll_ctl只是在往内核的数据结构里塞入新的socket句柄。
在内核里,一切皆文件。
所以,epoll向内核注册了一个文件系统,用于存储上述的被监控socket。
当你调用epoll_create时,就会在这个虚拟的epoll文件系统里创建一个file结点。
当然这个file不是普通文件,它只服务于epoll。
epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我们想监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。
这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象。
1staticint__init eventpoll_init(void)2{3
... ...45/*Allocates slab cache used to allocate "struct epitem" items*/6epi_cache = kmem_cache_create("eventpoll_epi",
sizeof(structepitem),70
, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,8NULL, NULL);910/*Allocates slab cache used to allocate "struct eppoll_entry"*/11pwq_cache = kmem_cache_create("eventpoll_pwq",12sizeof(structeppoll_entry),0,13EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL);1415
epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然可以飞快的返回,并有效的将发生事件的句柄给我们用户。
这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。
有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。
所以,epoll_wait非常高效。
那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。
所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。
如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。
执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。
执行epoll_wait时立刻返回准备就绪链表里的数据即可。
最后看看epoll独有的两种模式LT和ET。
无论是LT和ET模式,都适用于以上所说的流程。
区别是,LT模式下,只要一个句柄上的事件一次没有处理完,会在以后调用epoll_wait时次次返回这个句柄,而ET模式仅在第一次返回。
这件事怎么做到的呢?当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪list链表,这时我们调用epoll_wait,会把准备就绪的socket拷贝到用户态内存,然后清空准备就绪list链表,最后,epoll_wait干了件事,就是检查这些socket,如果不是ET模式(就是LT模式的句柄了),并且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。
所以,非ET的句柄,只要它上面还有事件,epoll_wait每次都会返回。
而ET模式的句柄,除非有新中断到,即使socket上的事件没有处理完,也是不会次次从epoll_wait返回的。
1/*2* Each file descriptor added to the eventpoll interface will3* have an entry of this type linked to the hash.4*/5structepitem {6/*RB-Tree node used to link this structure to the eventpoll rb-tree*/7structrb_node rbn;8//红黑树,用来保存eventpoll910/*List header used to link this structure to the eventpoll ready list*/11structlist_head rdllink;12//双向链表,用来保存已经完成的eventpoll1314/*The file descriptor information this item refers to*/15structepoll_filefd ffd;16//这个结构体对应的被监听的文件描述符信息1718/*Number of active wait queue attached to poll operations*/19intnwait;20//poll操作中事件的个数2122/*List containing poll wait queues*/23structlist_head pwqlist;24//双向链表,保存着被监视文件的等待队列,功能类似于select/poll中的poll_table2526/*The "container" of this item*/27structeventpoll *ep;28//指向eventpoll,多个epitem对应一个eventpoll2930/*The structure that describe the interested events and the source fd*/31structepoll_eventevent;32//记录发生的事件和对应的fd3334/*35* Used to keep track of the usage count of the structure. This avoids36* that the structure will desappear from underneath our processing.37*/38atomic_t t;39//引用计数4041/*List header used to link this item to the "struct file" items list*/42structlist_head fllink;43双向链表,用来链接被监视的文件描述符对应的struct file。
因为file里有f_ep_link,用来保存所有监视这个文件的epoll节点4445/*List header used to link the item to the transfer list*/46structlist_head txlink;47双向链表,用来保存传输队列4849/*50* This is used during the collection/transfer of events to userspace51* to pin items empty events set.52*/53unsignedintrevents;54//文件描述符的状态,在收集和传输时用来锁住空的事件集合55};5657//该结构体用来保存与epoll节点关联的多个文件描述符,保存的方式是使用红黑树实现的hash表.58//至于为什么要保存,下文有详细解释。
它与被监听的文件描述符一一对应.59structeventpoll {60/*Protect the this structure ess*/61rwlock_tlock;62//读写锁6364/*65* This semaphore is used to ensure that files are not removed66* while epoll is using them. This is read-held during the event67* collection loop and it is write-held during the file cleanup68* path, the epoll file exit code and the ctl operations.69*/70structrw_semaphore sem;71//读写信号量7273/*Wait queue used by sys_epoll_wait()*/74wait_queue_head_t wq;75/*Wait queue used by file->poll()*/7677wait_queue_head_t poll_wait;78/*List of ready file descriptors*/7980structlist_head rdllist;81//已经完成的操作事件的队列。
8283/*RB-Tree root used to store monitored fd structs*/84structrb_root rbr;85//保存epoll监视的文件描述符86};8788//这个结构体保存了epoll文件描述符的扩展信息,它被保存在file结构体的private_data89//中。
它与epoll文件节点一一对应。
通常一个epoll文件节点对应多个被监视的文件描述符。
90//所以一个eventpoll结构体会对应多个epitem结构体。
那么,epoll中的等待事件放在哪里呢?见下面91/*Wait structure used by the poll hooks*/92structeppoll_entry {93/*List header used to link this structure to the "struct epitem"*/94structlist_head llink;95/*The "base" pointer is set to the container "struct epitem"*/96void*base;97/*98* Wait queue item that will be linked to the target file wait99* queue head.
100*/101wait_queue_t wait;102/*The wait queue head that linked the "wait" wait queue item*/103wait_queue_head_t *whead;104};105106//与select/poll的struct poll_table_entry相比,epoll的表示等待队列节点的结107//构体只是稍有不同,与struct poll_table_entry比较一下。
108structpoll_table_entry {109structfile * filp;110wait_queue_t wait;111wait_queue_head_t * wait_address;112};Linux中select poll和epoll的区别
下面是select的函数接口:
int?select?(int?n,?fd_set?*readfds,?fd_set?*writefds,?fd_set?*exceptfds,?struct?timeval?*timeout);?
select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。
调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。
当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。
select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。
poll:
int?poll?(struct?pollfd?*fds,?unsigned?int?nfds,?int?timeout);?
不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。
struct?pollfd?{
int?fd;?/*?file?descriptor?*/
short?events;?/*?requested?events?to?watch?*/
short?revents;?/*?returned?events?witnessed?*/
};pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。
同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。
和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。
事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
epoll:
epoll的接口如下:
int?epoll_create(int?size);
int?epoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event?*event);
????????????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?*/
????????????};
int?epoll_wait(int?epfd,?struct?epoll_event?*?events,?int?maxevents,?int?timeout);复制代码
?
主要是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() 时便得到通知。
epoll的优点主要是一下几个方面:
1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
select的最大缺点就是进程打开的fd是有数量限制的。
这对 于连接数量比较大的服务器来说根本不能满足。
虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也 不是一种完美的方案。
2. IO的效率不会随着监视fd的数量的增长而下降。
epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。
只有就绪的fd才会执行回调函数。
3.支持电平触发和边沿触发(只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发)两种方式,理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
4.mmap加速内核与用户空间的信息传递。
epoll是通过内核于用户空间mmap同一块内存,避免了无畏的内存拷贝。
Linux内核中select,poll和epoll的区别
随着2.6内核对epoll的完全支持,网络上很多的文章和示例代码都提供了这样一个信息:使用epoll代替传统的poll能给网络服务应用带来性能上的提升。
但大多文章里关于性能提升的原因解释的较少,这里我将试分析一下内核(2.6.21.1)代码中poll与epoll的工作原理,然后再通过一些测试数据来对比具体效果。
POLL:
先说poll,poll或select为大部分Unix/Linux程序员所熟悉,这俩个东西原理类似,性能上也不存在明显差异,但select对所监控的文件描述符数量有限制,所以这里选用poll做说明。
poll是一个系统调用,其内核入口函数为sys_poll,sys_poll几乎不做任何处理直接调用do_sys_poll,do_sys_poll的执行过程可以分为三个部分:
1,将用户传入的pollfd数组拷贝到内核空间,因为拷贝操作和数组长度相关,时间上这是一个O(n)操作,这一步的代码在do_sys_poll中包括从函数开始到调用do_poll前的部分。
2,查询每个文件描述符对应设备的状态,如果该设备尚未就绪,则在该设备的等待队列中加入一项并继续查询下一设备的状态。
查询完所有设备后如果没有一个设备就绪,这时则需要挂起当前进程等待,直到设备就绪或者超时,挂起操作是通过调用schedule_timeout执行的。
设备就绪后进程被通知继续运行,这时再次遍历所有设备,以查找就绪设备。
这一步因为两次遍历所有设备,时间复杂度也是O(n),这里面不包括等待时间。
相关代码在do_poll函数中。
3,将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作,向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O(n),具体代码包括do_sys_poll函数中调用do_poll后到结束的部分。
EPOLL:
接下来分析epoll,与poll/select不同,epoll不再是一个单独的系统调用,而是由epoll_create/epoll_ctl/epoll_wait三个系统调用组成,后面将会看到这样做的好处。
先来看sys_epoll_create(epoll_create对应的内核函数),这个函数主要是做一些准备工作,比如创建数据结构,初始化数据并最终返回一个文件描述符(表示新创建的虚拟epoll文件),这个操作可以认为是一个固定时间的操作。
epoll是做为一个虚拟文件系统来实现的,这样做至少有以下两个好处:
1,可以在内核里维护一些信息,这些信息在多次epoll_wait间是保持的,比如所有受监控的文件描述符。
2, epoll本身也可以被poll/epoll;
具体epoll的虚拟文件系统的实现和性能分析无关,不再赘述。
在sys_epoll_create中还能看到一个细节,就是epoll_create的参数size在现阶段是没有意义的,只要大于零就行。
接着是sys_epoll_ctl(epoll_ctl对应的内核函数),需要明确的是每次调用sys_epoll_ctl只处理一个文件描述符,这里主要描述当op为EPOLL_CTL_ADD时的执行过程,sys_epoll_ctl做一些安全性检查后进入ep_insert,ep_insert里将 ep_poll_callback做为回掉函数加入设备的等待队列(假定这时设备尚未就绪),由于每次poll_ctl只操作一个文件描述符,因此也可以认为这是一个O(1)操作
ep_poll_callback函数很关键,它在所等待的设备就绪后被系统回掉,执行两个操作:
1,将就绪设备加入就绪队列,这一步避免了像poll那样在设备就绪后再次轮询所有设备找就绪者,降低了时间复杂度,由O(n)到O(1);
2,唤醒虚拟的epoll文件;
最后是sys_epoll_wait,这里实际执行操作的是ep_poll函数。
该函数等待将进程自身插入虚拟epoll文件的等待队列,直到被唤醒(见上面ep_poll_callback函数描述),最后执行ep_events_transfer将结果拷贝到用户空间。
由于只拷贝就绪设备信息,所以这里的拷贝是一个O(1)操作。
还有一个让人关心的问题就是epoll对EPOLLET的处理,即边沿触发的处理,粗略看代码就是把一部分水平触发模式下内核做的工作交给用户来处理,直觉上不会对性能有太大影响,感兴趣的朋友欢迎讨论。
POLL/EPOLL对比:
表面上poll的过程可以看作是由一次epoll_create/若干次epoll_ctl/一次epoll_wait/一次close等系统调用构成,实际上epoll将poll分成若干部分实现的原因正是因为服务器软件中使用poll的特点(比如Web服务器):
1,需要同时poll大量文件描述符;
2,每次poll完成后就绪的文件描述符只占所有被poll的描述符的很少一部分。
3,前后多次poll调用对文件描述符数组(ufds)的修改只是很小;Linux中select poll和epoll的区别
============select、poll、epoll之间的区别=================
select,poll,epoll都是IO多路复用的机制。
I/O多路复用就通过一种机制,
可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),
能够通知程序进行相应的读写操作,读写过程是阻塞的,
select的几大缺点:监视的文件描述符的数量存在最大限制
1 单个进程可监视的fd数量被限制
2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
3 对socket进行扫描时是线性扫描
//select的3个缺点:1 连接数受限 2 查找配对速度慢 3 数据由内核拷贝到用户态。
poll的实现和select非常相似,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)
select、poll的内部实现机制相似,它没有最大连接数的限制,原因是它是基于链表来存储的,
但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,
而不管这样的复制是不是有意义。
它将用户传入的数组拷贝到内核空间,
然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,这过程经历了多次无谓的遍历
epoll是对select和poll的改进
epoll:IO的效率不会随着监视fd的数量的增长而下降。
epoll不同于select和poll轮询的方式,
而是通过每个fd定义的回调函数来实现的。
只有就绪的fd才会执行回调函数。
一般情况下fd数量较少的时候poll略优于epoll,但是当fd增大到某个阈值时,
poll性能急剧下降。
而epoll始终保持的稳定的性能。
希望采纳
妮妮云的来历妮妮云是 789 陈总 张总 三方共同投资建立的网站 本着“良心 便宜 稳定”的初衷 为小白用户避免被坑妮妮云的市场定位妮妮云主要代理市场稳定速度的云服务器产品,避免新手购买云服务器的时候众多商家不知道如何选择,妮妮云就帮你选择好了产品,无需承担购买风险,不用担心出现被跑路 被诈骗的情况。妮妮云的售后保证妮妮云退款 通过于合作商的友好协商,云服务器提供2天内全额退款,超过2天不退款 物...
ZJI怎么样?ZJI是一家成立于2011年的商家,原名维翔主机,主要从事独立服务器产品销售,目前主打中国香港、日本、美国独立服务器产品,是一个稳定、靠谱的老牌商家。详情如下:月付/年付优惠码:zji??下物理服务器/VDS/虚拟主机空间订单八折终身优惠(长期有效)一、ZJI官网点击直达香港葵湾特惠B型 CPU:E5-2650L核心:6核12线程内存:16GB硬盘:480GB SSD带宽:5Mbps...
Boomer.Host是一家比较新的国外主机商,虽然LEB自述 we’re now more than 2 year old,商家提供虚拟主机和VPS,其中VPS主机基于OpenVZ架构,数据中心为美国得克萨斯州休斯敦。目前,商家在LET发了两款特别促销套餐,年付最低3.5美元起,特别提醒:低价低配,且必须年付,请务必自行斟酌确定需求再入手。下面列出几款促销套餐的配置信息。CPU:1core内存:...
epoll为你推荐
联想网盘联想网盘登陆settimer如何使用SetTimer MFC 够详细知识库管理系统什么是知识管理oracle索引Oracle中有多少种索引类型李昊天李昊天这名字给多少分layout_gravityandroid 布局中 为什么能够通过android:layout_above 、android:layout_alignTop 、等 还要在之前加入editplus破解版DBTools Manager Professional 破解版在哪里可以下载?vipjrvipjr英语怎么样?靠谱吗?问卷星登陆请问问卷星怎么设置答题时间?清除电脑垃圾怎样清除电脑垃圾
西安域名注册 便宜vps 国外免费域名网站 淘宝抢红包攻略 仿牌空间 ssh帐号 大容量存储 丹弗 小米数据库 促正网秒杀 阿里云浏览器 web服务器架设 gspeed 七夕快乐英文 hdd 100mbps vip域名 酸酸乳 登陆qq空间 supercache 更多