I/O多路转接之epoll

前言:

认识epoll。。

按照官方文档:
epoll:是为处理大批量句柄而做了改良的poll。
被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

和之前的select和poll不同的是:epoll使用三个函数共同实现。

epoll中的三个系统调用

1.I/O多路转接之epoll_第1张图片

目的:
创建了一个epoll模型。
究其本质:创建了一棵空的红黑树。(内核)

返回值:epoll模型的句柄。

注:size参数可以不用关心。

2.
I/O多路转接之epoll_第2张图片

目的:
维护epoll模型(本质是:增删查改红黑树中的节点等)。

函数描述:
epoll的事件注册函数,也就是关心哪些文件描述符中的哪些事件(本质就是向红黑树中添加节点以及修改节点还是删除节点)。
不同于select是在监听事件时告诉内核要监听什么类型的事件,在这里epoll是先注册要监听的事件类型。

参数说明:
A、epfd:epoll模型句柄
B、op:表示动作,对于红黑树的增删改。
C、fd:表示需要监听的fd。
D、events:告诉内核要监听什么事。
(C和D表示要关心哪些文件描述符中的哪些事件,红黑树中的节点中的key值就是所关心的文件描述符,而对应的value就是这些文件描述符所关心的哪些事件)。
events可以是下面几个宏的结合:

EPOLLIN : 表示对应的文件描述符可以读
EPOLLOUT : 表示对应的文件描述符可以写。
EPOLLPRI : 表示对应的文件描述符有紧急的数据可读(表示带外数据到来);
EPOLLERR : 表示对应的文件描述符发生错误。
EPOLLHUP:表⽰示对应的⽂文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于⽔水平触发(LevelTriggered)来说的。
EPOLLONESHOT:只监听⼀一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加⼊入到EPOLL队列⾥里

3.
I/O多路转接之epoll_第3张图片
函数描述:
收集所关心的文件描述符上的一些就绪的特定的事件。

参数:
events:是一个结构体数组,同时也是一个输出型参数,epoll将就绪事件复制到events数组中。
(events不可以为空指针,内核只负责把数据复制到这个events数组中,不会帮我们在用户态中分配内存)。

maxevents标记前面结构体数组的大小

timeout:超时时间(0—》立即返回 -1—-》出错返回)。

返回值:成功返回(大于0)已经就绪的文件描述符的个数;
利用该返回值可以遍历就绪队列。
返回0表示已超时。

这里使用了内存映射(mmap)技术,这样便能省掉了这些文件描述符在系统调用时复制的开销。可以将上面函数参数中的events当做是内核维护的一个就绪队列,这样就不存在将数据从内核拷贝到用户,再从用户拷贝至内核所浪费的资源。


epoll比起select和poll的优势:

在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描;
而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核就会采用类似callback的回调机制,迅速激活这个文件描述符,(实质指的是将红黑树中的一个节点拷贝至就绪队列中),当进程调用epoll_wait()时便得到通知。


图示了解底层实现机制:

I/O多路转接之epoll_第4张图片

解释:
A:epoll_create()完成了下面的工作:
1.创建了一个空的红黑树;
2.维护了一个就绪队列(此时是空的)。

B:epoll_ctl()完成了下面的工作:
向红黑树中添加节点(添加的重复的事件通过红黑树可以有效的检测出来),该节点中最重要的成员就是所关心的文件描述符和该文件描述符对应的事件。

当向红黑树中添加节点后,还会做一件事情,还会给内核的中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它添加到就绪队列中。
所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪队列中了。

C:epoll_wait()完成的工作:
只是检测就绪队列中是否有数据,如果有数据就直接返回,如果没有事件就绪(即该队列为空)时就sleep,直到timeout时间后返回(不论事件是否就绪了)。

小结:
一颗红黑树,一个准备就绪队列,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪队列,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪队列中添加数据。
执行epoll_wait时立刻返回准备就绪队列中的数据即可。


小结:
问什么epoll较之poll和select比较高效呢?

1.底层维护了红黑树(增删改查的时间复杂度可以控制在logN);

2.采用了回调机制,将就绪事件激活至就绪队列中;

3.就绪队列的数据陈列是连续的(使得从就绪队列中取出数据的时间复杂度为O(1))。并且利用epoll_wait()的返回值就可以遍历完就绪队列中的所有的就绪事件以及文件描述符。

4.采用内核映射机制。(不用在内核和用户之间不断的拷贝数据)。

你可能感兴趣的:(Linux,socket编程应用,epoll)