epoll、cpoll、xpoll

xserver:高性能网络库,很牛叉的一个server,有4中工作模式:xpoll、epoll、cpoll、apoll。
 
select、poll、epoll、xpoll、cpoll、apoll:IO多路复用的机制,监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。
 
I/O多路复用:关于I/O多路复用(又被称为“事件驱动”),首先要理解的是,操作系统为你提供了一个功能,当你的某个socket可读或者可写的时候,它可以给你一个通知。这样当配合非阻塞的socket使用时,只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。写操作类似。操作系统的这个功能通过select/poll/epoll/kqueue之类的系统调用函数来使用,这些函数都可以同时监视多个描述符的读写就绪状况,这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程

I/O 多路复用是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程或线程不阻塞于某个特定的 I/O 系统调用。select(),poll(),epoll()都是I/O多路复用的机制。I/O多路复用通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),能够通知程序进行相应的读写操作。但select(),poll(),epoll()本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

与多线程(TPC(Thread Per Connection)模型)和多进程(典型的Apache模型(Process Per Connection,简称PPC))相比,I/O 多路复用的最大优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。

          

 

 

                                                                      I/O多路复用

长连接&短连接

       1、长连接:管子搭好之后不会主动close,accept read write read write read write ...
             应用场景:适合client比较固定(客户规定),返回请求多次的场景。网络是不可靠的,所以长连接有个心跳检测的过程。每keepalive_timeout检测一次心跳,没检测到则close;
        2、短连接:连好进行了一次读写后就close,accept read/write close
             应用场景:适合client不固定,只请求一次的场景。

 select?EPool?:

        1、select:

              1)FD_ZERO: void FD_ZERO(fd_set *fdset);清空文件描述符集合;

              2)FD_SET: void FD_SET(int fd, fd_set *fdset); 将一个给定的文件描述符加入集合之中;

              3)select:返回就绪描述符的个数

select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

              4)read/write;send/recv:处理事件

              select轮询的管子(socket、文件描述符)数目有限,默认为1024,即使把他扩大到1000,效率也会非常差:

                  a)每次,用户空间到内核空间的拷贝,拷贝时间空间都是o(n);

                  b)o(n)轮询(内核空间)

                  c)内核到用户空间的再次拷贝

                  d)再次o(n)轮询(用户空间)

               当然,如果管口比较少的时候,select还是有用的。

         2、epoll:

               1)epoll_create: int epoll_create(int size);创建一个epool的句柄,size用来告诉内核这个监听的数目一共有多大,这个参数是最大监听的fd+1的值。当创建好epoll句柄后,它会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽;

               2)epoll_ctl: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll的事件注册函数,它不同于slect是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来标识:

                  EPOLL_CTL_ADD:注册新的fd到epfd中;

                  EPOLL_CTL_MOD: 修改已经注册的fd的监听事件;

                  EPOLL_CTL_DEL: 从epfd中删除一个fd;

                  第三个参数是要监听的fd,第四个参数是告诉内核需要监听什么事,

               3)epoll_wait: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);等待事件发生,类似于select()的调用。参数events用来从内核得到事件的集合,maxevents告知内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定)。该函数返回需要处理的事件数目,如返回0表示已超时。

               4)read/write; send/recv:

                不限制可以监控的管子数目;一个进程所支持的FD上限是最大可以打开文件的数目,1G内存机器上限大约是10W。

                常数级别

                       a) 用户空间和内核使用mmap进行文件描述符的交流,省去拷贝;

                       b) 红黑树 插入时间o(lgn)

                       c) 就绪队列(callback)

                  epoll对比select的优点:

                  1) 针对select第一步的每次o(n)的拷贝,使用了mmap方法;

                  2) 针对内核o(n)的遍历,引入了两个结构,第一个数红黑树,每次只需要加入感兴趣的管子,不必拷贝所有的管子,而且插入删除的时间复杂度都是o(n);第二是引入了一个就绪队列,这个就绪对别的操作都是通过callback实现的,每当某个管子上注册的事件发生,就把这个管子放到就绪队列里,这个就绪队列里只有活跃的管子,可能是o(n)也可能是o(1);

xpool:领导者、跟随者模式;

 

epoll:生产者、消费者模式:

 

cpoll:类似epoll,但是没有红黑屋,不是主动询问,而是被动告知:

由主人去询问管子是否有需求。说白了就是通过select实现io多路复用,剩下的和xs_epoll是一样的。

 

 apoll:给active的需求管子提供服务:

和epool基本是一致的,只不过由仆人做的2个工作中,主人主动承担了第一个工作:读管子;

 

参考资料epoll:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

参考资料epoll:http://blog.csdn.net/lingfengtengfei/article/details/12398299

参考资料select:http://www.cnblogs.com/Anker/p/3258674.html

posted on 2017-10-02 15:32  月未央  阅读(853)  评论(0编辑  收藏  举报

导航