epoll与select/poll不同的一点是,它是由一组系统调用组成。
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相关系统调用是在Linux 2.5.44开始引入的。该系统调用针对传统的selec
t/poll系统调用的不足,设计上作了很大的改动。select/poll的缺点在于:
1.每次调用时要重复地从用户态读入参数。
2.每次调用时要重复地扫描文件描述符。
3.每次在调用开始时,要把当前进程放入各个文件描述符的等待队列。在调用结
束后,又把进程从各个等待队列中删除。
在实际应用中,select/poll监视的文件描述符可能会非常多,如果每次只是返回
一小部分,那么,这种情况下select/poll显得不够高效。epoll的设计思路,是把s
elect/poll单个的操作拆分为1个epoll_create+多个epoll_ctrl+一个wait。此外,
内核针对epoll操作添加了一个文件系统”eventpollfs”,每一个或者多个要监视的
文件描述符都有一个对应的eventpollfs文件系统的inode节点,主要信息保存在eve
ntpoll结构体中。而被监视的文件的重要信息则保存在epitem结构体中。所以他们
是一对多的关系。
由于在执行epoll_create和epoll_ctrl时,已经把用户态的信息保存到内核态了
,所以之后即使反复地调用epoll_wait,也不会重复地拷贝参数,扫描文件描述符,
反复地把当前进程放入/放出等待队列。这样就避免了以上的三个缺点。
接下去看看它们的实现:
2 关键结构体:
/* Wrapper struct used by poll queueing */
struct ep_pqueue {
poll_table pt;
struct epitem *epi;
};
这个结构体类似于select/poll中的struct poll_wqueues。由于epoll需要在内核
态保存大量信息,所以光光一个回调函数指针已经不能满足要求,所以在这里引入了
一个新的结构体struct epitem。
/*
* Each file descriptor added to the eventpoll interface will
* have an entry of this type linked to the hash.
*/
struct epitem {
/* RB-Tree node used to link this structure to the eventpoll rb
-tree */
struct rb_node rbn;
红黑树,用来保存eventpoll
/* List header used to link this structure to the eventpoll rea
dy list */
struct list_head rdllink;
双向链表,用来保存已经完成的eventpoll
/* The file descriptor information this item refers to */
struct epoll_filefd ffd;
这个结构体对应的被监听的文件描述符信息
/* Number of active wait queue attached to poll operations */
int nwait;
poll操作中事件的个数
/* List containing poll wait queues */
struct list_head pwqlist;
双向链表,保存着被监视文件的等待队列,功能类似于select/poll中的poll_tab
le
/* The "container" of this item */
struct eventpoll *ep;
指向eventpoll,多个epitem对应一个eventpoll
/* The structure that describe the interested events and the so
urce fd */
struct epoll_event event;
记录发生的事件和对应的fd
/*
* Used to keep track of the usage count of the structure. This
avoids
* that the structure will desappear from underneath our proces
sing.
*/
atomic_t usecnt;
引用计数
/* List header used to link this item to the "struct file" item
s list */
struct list_head fllink;
双向链表,用来链接被监视的文件描述符对应的struct file。因为file里有f_ep
_link,用来保存所有监视这个文件的epoll节点
/* List header used to link the item to the transfer list */
struct list_head txlink;
双向链表,用来保存传输队列
/*
* This is used during the collection/transfer of events to use
rspace
* to pin items empty events set.
*/
unsigned int revents;
文件描述符的状态,在收集和传输时用来锁住空的事件集合
};
该结构体用来保存与epoll节点关联的多个文件描述符,保存的方式是使用红黑树
实现的hash表。至于为什么要保存,下文有详细解释。它与被监听的文件描述符一一
对应。
struct eventpoll {
/* Protect the this structure access */
rwlock_t lock;
读写锁
/*
* This semaphore is used to ensure that files are not removed
* while epoll is using them. This is read-held during the even
t
* collection loop and it is write-held during the file cleanup
* path, the epoll file exit code and the ctl operations.
*/
struct rw_semaphore sem;
读写信号量
/* Wait queue used by sys_epoll_wait() */
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
struct list_head rdllist;
已经完成的操作事件的队列。
/* RB-Tree root used to store monitored fd structs */
struct rb_root rbr;
保存epoll监视的文件描述符
};
这个结构体保存了epoll文件描述符的扩展信息,它被保存在file结构体的priva
te_data中。它与epoll文件节点一一对应。通常一个epoll文件节点对应多个被监视
的文件描述符。所以一个eventpoll结构体会对应多个epitem结构体。
那么,epoll中的等待事件放在哪里呢?见下面
/* Wait structure used by the poll hooks */
struct eppoll_entry {
/* List header used to link this structure to the "struct epite
m" */
struct list_head llink;
/* The "base" pointer is set to the container "struct epitem" *
/
void *base;
/*
* Wait queue item that will be linked to the target file wait
* queue head.
*/
wait_queue_t wait;
/* The wait queue head that linked the "wait" wait queue item *
/
wait_queue_head_t *whead;
};
与select/poll的struct poll_table_entry相比,epoll的表示等待队列节点的结
构体只是稍有不同,与struct poll_table_entry比较一下。
struct poll_table_entry {
struct file * filp;
wait_queue_t wait;
wait_queue_head_t * wait_address;
};
由于epitem对应一个被监视的文件,所以通过base可以方便地得到被监视的文件
信息。又因为一个文件可能有多个事件发生,所以用llink链接这些事件。
3 epoll_create的实现
epoll_create()的功能是创建一个eventpollfs文件系统的inode节点。具体由ep
_getfd()完成。ep_getfd()先调用ep_eventpoll_inode()创建一个inode节点,然后
调用d_alloc()为inode分配一个dentry。最后把file,dentry,inode三者关联起来。
在执行了ep_getfd()之后,它又调用了ep_file_init(),分配了eventpoll结构体
,并把eventpoll的指针赋给file结构体,这样eventpoll就与file结构体关联起来了
。
需要注意的是epoll_create()的参数size实际上只是起参考作用,只要它不小于
等于0,就并不限制这个epoll inode关联的文件描述符数量。
4 epoll_ctl的实现
epoll_ctl的功能是实现一系列操作,如把文件与eventpollfs文件系统的inode节
点关联起来。这里要介绍一下eventpoll结构体,它保存在file->f_private中,记录
了eventpollfs文件系统的inode节点的重要信息,其中成员rbr保存了该epoll文件节
点监视的所有文件描述符。组织的方式是一棵红黑树,这种结构体在查找节点时非常
高效。
首先它调用ep_find()从eventpoll中的红黑树获得epitem结构体。然后根据op参
数的不同而选择不同的操作。如果op为EPOLL_CTL_ADD,那么正常情况下epitem是不
可能在eventpoll的红黑树中找到的,所以调用ep_insert创建一个epitem结构体并插
入到对应的红黑树中。
ep_insert()首先分配一个epitem对象,对它初始化后,把它放入对应的红黑树。
此外,这个函数还要作一个操作,就是把当前进程放入对应文件操作的等待队列。这
一步是由下面的代码完成的。
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
。。。
revents = tfile->f_op->poll(tfile, &epq.pt);
函数先调用init_poll_funcptr注册了一个回调函数 ep_ptable_queue_proc,这
个函数会在调用f_op->poll时被执行。该函数分配一个epoll等待队列结点eppoll_e
ntry:一方面把它挂到文件操作的等待队列中,另一方面把它挂到epitem的队列中
。此外,它还注册了一个等待队列的回调函数ep_poll_callback。当文件操作完成,
唤醒当前进程之前,会调用ep_poll_callback(),把eventpoll放到epitem的完成队
列中,并唤醒等待进程。
如果在执行f_op->poll以后,发现被监视的文件操作已经完成了,那么把它放在
完成队列中了,并立即把等待操作的那些进程唤醒。
5 epoll_wait的实现
epoll_wait的工作是等待文件操作完成并返回。
它的主体是ep_poll(),该函数在for循环中检查epitem中有没有已经完成的事件
,有的话就把结果返回。没有的话调用schedule_timeout()进入休眠,直到进程被再
度唤醒或者超时。
6 性能分析
epoll机制是针对select/poll的缺陷设计的。通过新引入的eventpollfs文件系统
,epoll把参数拷贝到内核态,在每次轮询时不会重复拷贝。通过把操作拆分为epol
l_create,epoll_ctl,epoll_wait,避免了重复地遍历要监视的文件描述符。此外,
由于调用epoll的进程被唤醒后,只要直接从epitem的完成队列中找出完成的事件,
找出完成事件的复杂度由O(N)降到了O(1)。
但是epoll的性能提高是有前提的,那就是监视的文件描述符非常多,而且每次完
成操作的文件非常少。所以,epoll能否显著提高效率,取决于实际的应用场景。这
方面需要进一步测试。
分享到:
相关推荐
### epoll原理与实现 #### 一、概述 `epoll`是Linux系统中高效地进行I/O多路复用的一种机制,它相比之前的`select()`和`poll()`提供了更好的性能和扩展性。本文将深入探讨`epoll`的工作原理、内部实现细节以及如何...
epoll 工作原理详解 epoll 是一种高效的 IO 多路复用技术,可以处理数以百万计的 socket 句柄。它的高效性来自于其巧妙的设计和实现。下面我们将深入探讨 epoll 的工作原理。 epoll_create epoll_create 是建立一...
本文将解析VPP中epoll实现的代码以及与上层应用交互流程的相关知识点。 首先,我们需要理解VPP中epoll的基本工作原理。VPP通过一个事件队列(event_q)来与上层应用进行通信。当网络层接收到报文时,会通过epoll机制...
这个名为"epoll实现cs通信.zip"的压缩包包含了一组用于演示如何使用epoll实现客户端(Client)与服务器端(Server)通信的源代码文件。下面将详细介绍epoll的工作原理以及压缩包中的文件内容。 1. **epoll的工作...
通过分析这个“Linux下epoll实现简易聊天”的源代码,你可以深入理解epoll的工作原理以及如何在实际项目中运用它。同时,这也是学习多线程编程、网络编程、套接字通信、数据传输协议等基础知识的好实践。在客户端和...
首先,`epoll`的工作原理是基于事件驱动的,它提供了一个接口,可以监控多个文件描述符(如socket)的状态变化,当这些描述符上有可读写事件发生时,`epoll_wait`函数会返回相应的事件,从而避免了传统`poll`或`...
首先,我们来理解`epoll`的工作原理。`epoll`基于`IO多路复用`技术,它提供了一个接口,允许程序注册一组文件描述符(如套接字),然后等待这些描述符上的事件发生。当有事件发生时,`epoll_wait`函数会返回就绪的...
:smiling_face_with_smiling_eyes:感谢王博靖前辈的开源精神,让我对epoll实现原理有了深刻的理解,在这里对他表示非常的感谢! 一,目录介绍 (后期补充) 二,整体框架 (后期补充) 三,更细日志 1、2019-10-25 ...
`epoll`是Linux系统提供的一...总结来说,`epoll`是一种优化的I/O事件通知机制,通过红黑树和双向链表高效地管理描述符,配合`epoll_ctl()`和`epoll_wait()`实现对事件的监听和处理,特别适合处理大量并发连接的情况。
这些函数在不同场景下各有优劣,理解它们的实现原理有助于选择合适的解决方案。 1. **select** - `select`函数是最早引入的多路复用I/O机制,适用于小规模的文件描述符集。在Linux内核中,`select`的核心工作在于...
首先,我们需要理解`epoll`的工作原理。`epoll`采用了“事件”的概念,通过`epoll_create`函数创建一个`epoll`实例,然后使用`epoll_ctl`添加感兴趣的文件描述符(FD),并指定监听的事件类型(如EPOLLIN表示可读,...
`epoll`是Linux系统提供的一种I/O复用机制,它是I/O多路复用技术的一种优化实现,特别是在高并发场景下表现优秀。本篇将深入探讨`epoll`的工作原理、两种触发模式以及其在实际应用中的价值。 首先,理解`epoll`的...
`linux下的Tcp服务器,采用的是Epoll模式,deamon程序,适合大家.rar`和`.tar`文件提供了实际的Epoll TCP服务器示例,而`linux下用epoll实现的udp服务器例子,很精简和实用.rar`则展示了Epoll在UDP服务器中的应用。...
在IT领域,网络编程是不可或缺的一部分,特别是在分布式系统和跨平台通信中。本文将深入探讨如何使用Linux C...在开发过程中,需要充分考虑平台差异,合理利用epoll和Winsock提供的功能,以实现稳定可靠的跨平台通信。
### Epoll原理详解 #### 一、概述 `epoll`是现代Linux系统中的一个重要特性,主要用于高效地处理大量并发连接。相比于传统的`select`和`poll`,`epoll`提供了一种更为高效的事件通知机制,使得服务器能够有效地...
二、epoll工作原理 1. `epoll_create`: 创建一个`epoll`实例,返回一个文件描述符。 2. `epoll_ctl`: 注册或修改文件描述符到`epoll`实例中,可以设置监听的事件类型(如读、写、错误等)。 3. `epoll_wait`: 阻塞...
本篇文章将详细探讨如何在Linux环境下利用Epoll系统调用来实现一个高性能的Web服务器。 Epoll是I/O多路复用技术的一种,它改进了早期的select和poll方法。Epoll提供了“边缘触发”(ET)和“水平触发”(LT)两种模式,...
使用epoll机制实现并发,增加效率。 使用fork创建两个进程,一个为写进程,一个为读进程。 将聊天信息写到管道(pipe),并发送给父进程。 使用epoll机制接受服务端发来的信息,并显示给用户,使用户看到其他用户的...
这里我们将探讨如何将FIFO与管道PIPE结合使用,以实现FIFO传递数据,并利用epoll实现异步I/O的效果。 首先,理解FIFO的工作原理至关重要。FIFO是一个命名管道,它存在于文件系统中,可以被任何具有相应权限的进程...