select,poll.epoll区别于联系
select,poll,epoll都是IO多路复用中的模型。再介绍他们特点时,先来看看多路复用的 模型。

同其他IO的不同的是,IO多路复用一次可以等多个文件描述符。大大提高了等待数据准备好的时间的效率。为了完成等的效率,系统提供了三个系统调用:select,poll,epoll。这里不再讲述三者具体实现,只总结三者的优缺点。
select的缺点
1.单个进程监控的文件描述符有限,通常为1024*8个文件描述符。当然可以改进,由于select采用轮询方式扫描文件描述符。文件描述符数量越多,性能越差。
2.内核/用户数据拷贝频繁,操作复杂。select在调用之前,需要手动在应用程序里将要监控的文件描述符添加到fed_set集合中。然后加载到内核进行监控。用户为了检测时间是否发生,还需要在用户程序手动维护一个数组,存储监控文件描述符。当内核事件发生,在将fed_set集合中没有发生的文件描述符清空,然后拷贝到用户区,和数组中的文件描述符进行比对。再调用selecct也是如此。每次调用,都需要了来回拷贝。
3.轮回时间效率低。select返回的是整个数组的句柄。应用程序需要遍历整个数组才知道谁发生了变化。轮询代价大。
4、select是水平触发。应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作。那么之后select调用还是会将这些文件描述符返回,通知进程。
poll特点
  1.poll操作比select稍微简单点。select采用三个位图来表示fd_set,poll使用pollfd的指针,pollfd结构包含了要监视的event和发生的evevt,不再使用select传值的方法。更方便
2.select的缺点依然存在。拿select为例,加入我们的服务器需要支持100万的并发连接。则在FD_SETSIZE为1024的情况下,我们需要开辟100个并发的进程才能实现并发连接。除了进程上下调度的时间消耗外。从内核到用户空间的无脑拷贝,数组轮询等,也是系统难以接受的。因此,基于select实现一个百万级别的并发访问是很难实现的。
epoll模型
  由于epoll和上面的实现机制完全不同,所以上面的问题将在epoll中不存在。在select/poll中,服务器进程每次调用select都需要把这100万个连接告诉操作系统(从用户态拷贝到内核态)。让操作系统检测这些套接字是否有时间发生。轮询完之后,再将这些句柄数据复制到操作系统中,让服务器进程轮询处理已发生的网络时间。这一过程耗时耗力,而epoll通过在linux申请一个建议的文件系统,把select调用分为了三部分。
  1)调用epoll_create建立一个epoll对象,这个对象包含了一个红黑树和一个双向链表。并与底层建立回调机制。
  2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
  3)调用epoll_wait收集发生事件的连接。
从上面的调用方式就可以看到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的内存对象,每次使用时都是使用空闲的已分配好的对象。
epoll的高效
epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然可以飞快的返回,并有效的将发生事件的句柄给我们用户。这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。而且,通常情况下即使我们要监控百万计的句柄,大多一次也只返回很少量的准备就绪句柄而已,所以,epoll_wait仅需要从内核态copy少量的句柄到用户态而已,如何能不高效?!
就绪list链表维护
  那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。
如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。
两种模式LT和ET
最后看看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返回的。
其中涉及到的数据结构:
epoll用kmem_cache_create(slab分配器)分配内存用来存放struct epitem和struct eppoll_entry。
当向系统中添加一个fd时,就创建一个epitem结构体,这是内核管理epoll的基本数据结构:
struct epitem {
    struct rb_node rbn;          //用于主结构管理的红黑树
    struct list_head rdllink;    //事件就绪队列
    struct epitem *next;         //用于主结构体中的链表
    struct epoll_filefd ffd;     //这个结构体对应的被监听的文件描述符信息
    int nwait;                   //poll操作中事件的个数
    struct list_head pwqlist;    //双向链表,保存着被监视文件的等待队列,功能类似于select/poll中的poll_table
    struct eventpoll *ep;        //该项属于哪个主结构体(多个epitm从属于一个eventpoll)
    struct list_head fllink;     //双向链表,用来链接被监视的文件描述符对应的struct file。因为file里有f_ep_link,用来保存所有监视这个文件的epoll节点
    struct epoll_event event;    //注册的感兴趣的事件,也就是用户空间的epoll_event
}
而每个epoll fd(epfd)对应的主要数据结构为:
struct eventpoll {
spin_lock_t lock; //对本数据结构的访问
struct mutex mtx; //防止使用时被删除
wait_queue_head_t wq; //sys_epoll_wait()使用的等待队列
wait_queue_head_t poll_wait; //file->poll()使用的等待队列
struct list_head rdllist; //事件满足条件的链表 /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
struct rb_root rbr; //用于管理所有fd的红黑树(树根)/*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
struct epitem *ovflist; //将事件到达的fd进行链接起来发送至用户空间
}
  struct eventpoll在epoll_create时创建。
  这样说来,内核中维护了一棵红黑树,大致的结构如下:

整体而言:

select,poll.epoll区别于联系的更多相关文章
- select,poll,epoll区别
		
select:忙轮询,一直在轮询,效率跟链接数成反比,资源限制 poll:轮询,不用一直轮询,有事件触发时轮询,资源限制 epoll:有事件触发时直接通知复杂度O(1)
 - 哪5种IO模型?什么是select/poll/epoll?同步异步阻塞非阻塞有啥区别?全在这讲明白了!
		
系统中有哪5种IO模型?什么是 select/poll/epoll?同步异步阻塞非阻塞有啥区别? 本文地址http://yangjianyong.cn/?p=84转载无需经过作者本人授权 先解开第一个 ...
 - select.poll,epoll的区别与应用
		
先讲讲同步I/O的五大模型 阻塞式I/O, 非阻塞式I/O, I/O复用,信号驱动I/O(SIGIO),异步I/O模型 而select/poll/epoll属于I/O复用模型 select函数 该函数 ...
 - Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)
		
一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...
 - select,poll,epoll的归纳总结区分
		
Select.Poll与Epoll比较 以下资料都是来自网上搜集整理.引用源详见文章末尾. 1 Select.Poll与Epoll简介 Select select本质上是通过设置或者检查存放fd标志位 ...
 - select, poll, epoll的实现分析
		
select, poll, epoll都是Linux上的IO多路复用机制.知其然知其所以然,为了更好地理解其底层实现,这几天我阅读了这三个系统调用的源码. 以下源代码摘自Linux4.4.0内核. 预 ...
 - 转--select/poll/epoll到底是什么一回事
		
面试题:说说select/poll/epoll的区别. 这是面试后台开发时的高频面试题,属于网络编程和IO那一块的知识.Android里面的Handler消息处理机制的底层实现就用到了epoll. 为 ...
 - IO多路复用select/poll/epoll详解以及在Python中的应用
		
IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...
 - python 套接字之select poll epoll
		
python下的select模块使用 以及epoll与select.poll的区别 先说epoll与select.poll的区别(总结) select, poll, epoll 都是I/O多路复用的具 ...
 
随机推荐
- spring整合mybatis(非代理方式)【我】
			
首先创建要给 maven 的war项目 不用代理的方式: 如果不适用Mapper代理的方式,配置就非常简单: 首先是pom文件(以下配置文件包含其他多余内容,仅供参考): <project xm ...
 - ArrayList去除集合中字符串的重复值
			
package com.heima.list; import java.util.ArrayList; import java.util.Iterator; import java.util.List ...
 - Redis集群配置和常见异常解决
			
前文 Redis的Cluster集群,是在分布式且开源环境下最佳的高可用解决方案,可以有效的解决服务器宕机下或高并发下,数据的完整性. 文档前提 Redis 3.0版本或更高版本.(3.0版本开始支持 ...
 - CompletableFuture.allOf that doens't return Void(CompletableFuture.allOf不能返回Void的解决方法)
			
import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Ti ...
 - 使用MJRefresh自定义下拉刷新,上拉加载动画
			
有时候我们需要自己设置下拉刷新,上拉加载动画的实现,这里主要是记录下使用MJRefresh自定义下拉刷新,上拉加载动画..... 下拉刷新我们只需要继承MJRefreshGifHeader即可: 实现 ...
 - Ansible 直接请求远程主机执行命令
			
ansible -all -i host1.abc.com, -m ping #注意主机名称后面的逗号,就算一台主机也是必须的.多台主机可以用逗号隔开 ansible all -i host1.abc ...
 - JavaScript基础之数组常用方法
			
目录 JS 数组常用API 常用属性 常用方法 常见方法语法解释 from方法 isArray concat every fill filter find forEach indexOf join k ...
 - 分享一款架子鼓游戏【drum】
			
视频演示: 电脑版和安卓版都有,没有ios版. 适合想练架子鼓,但是还没买架子鼓的朋友.可以先当做游戏来练练节奏感...下载如下: 电脑版下载>> 手机安卓版下载>>
 - 微信小程序遍历wx:for,wx:for-item,wx:key
			
微信小程序中wx:for遍历默认元素为item,但是如果我们设计多层遍历的时候我们就需要自定义item的字段名以及key的键名 wx:for="{{item.goodsList}}" ...
 - 【HANA系列】【第二篇】SAP HANA XS使用JavaScript编程详解
			
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第二篇]SAP HANA XS ...