epoll详解

首先要知道,selectpollepoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但selectpollepoll本质上都是同步非阻塞I/O,因为他们都需要在读写事件就绪后自己负责进行读写,而异步I/O则无需自己负责进行读写,内核来完成,然后会收到内核的信号,进程就会知道IO完成了。

 

关于阻塞非阻塞,同步异步

阻塞IO和非阻塞IO其实是针对是否会阻塞于等待IO就绪如connect,read,write来说的,阻塞IO会一直阻塞在原地,直到返回,如java中的socket.read(),而非阻塞IO是指这个IO事件会立即返回,无论成功还是失败,一般会不段轮询的去调用该函数来判断IO事件是否就绪了,如java nio。

附:非阻塞通过不断检查事件的状态来判断是否进行读写操作,会带来一定的开销

 

同步IO和异步IO则是针对具体IO操作的处理是否由内核来完成来说的,同步IO是需要自己完成读写的过程,,但是异步IO是把读写的任务交给内核去完成,完成后自己会收到一个信号,完成后通常采用回调的方式来获得通知,如java aio。

epoll虽然也用到了回调,但是回调只是将事件就绪的fd放在了链表中,最后的IO操作还是进程本身去处理的,所以是同步IO

 

接下来进入正题

我们所说的epoll其实是三个方法,这里先简单的做个介绍,即

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd,struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event*events,int maxevents, int timeout);

 

epoll的整个过程(不是源码分析)

epoll_create会返回一个epollfd也就是epoll句柄,在此期间,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

eventpoll结构体如下所示:

struct eventpoll{
    ....
    /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
    struct rb_root  rbr;
    /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
    struct list_head rdlist;
    ....
};


每一个epoll句柄都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来,而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

而红黑树和就绪句柄链表就是epoll高效和支持高并发的基础。

在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示

struct epitem{
    struct rb_node  rbn;//红黑树节点
    struct list_head    rdllink;//双向链表节点
    struct epoll_filefd  ffd;  //事件句柄信息
    struct eventpoll *ep;    //指向其所属的eventpoll对象
    struct epoll_event event; //期待发生的事件类型
}

 

epoll_ctl可以操作上面返回的epollfd,例如,将刚建立的socket fd加入到epoll中让其监控,或者把 epoll正在监控的某个socket fd移出epoll,不再监控它等等。

epoll_wait通常是在循环体里被调用,在给定的timeout时间内,当在监控的这些文件描述符中的某些文件描述符上有事件发生时(也就是就绪链表中),就返回用户态的进程。

 

epoll的两种工作模式

epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

 

LT, ET怎么实现的呢?当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪list链表,这时我们调用epoll_wait,会把准备就绪的socket拷贝到用户态内存,然后清空准备就绪list链表,最后,epoll_wait干了件事,就是检查这些socket,如果是LT模式,并且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。所以,非ET的句柄,只要它上面还有事件,epoll_wait每次都会返回这个句柄,效率会低一点。


附:为什么epoll能极其高效地实现百万级socket监听


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值