1. 简介:
本文将介绍内核epoll实现的原理。基于kernel 2.6.32版本。
本文只描述epoll对其他fd的监听,由于epoll本身也是一种文件系统,也可以被监听,这一部分不在这里介绍。
2. 基础数据结构:
epoll中主要数据结构有两个,一个是epoll_create创建的epoll_fd的结构体eventpoll,一个是事件源对应的epitem结构体。
epollevent的数据结构及相应解释如下:这里注意的是eventpoll中其实有两个ready list,一个是常规的rdllist,还有一个是ovflist,两个队列的区别是ovflist用于当前epoll已经在将ready list发送到用户空间时,这时候设备状态改变唤醒的时候不能直接添加在ready list中,而是需要添加在ovflist,当ready list处理完时候再从ovflist移到ready list,相当于一个备用的队列。
struct eventpoll {
/* Protect the this structure access */
spinlock_t lock;
/*
* This mutex is used to ensure that files are not removed
* while epoll is using them. This is held during the event
* collection loop, the file cleanup path, the epoll file exit
* code and the ctl operations.
*/
struct mutex mtx;
/* Wait queue used by sys_epoll_wait() */
/* 用于epoll_wait时等待事件激活时让出本进程调度权限时候的等待队列。 */
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
/* 用于epoll这个文件类型的对应的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 */
/* 管理所有事件源,用于epoll_ctl中查找epitem。 */
struct rb_root rbr;
/*
* This is a single linked list that chains all the "struct epitem" that
* happened while transfering ready events to userspace w/out
* holding ->lock.
*/
/* 用于当epoll准备将数据返回给用户时候(ep_send_events_proc),
* 这时候设备状态改变回调(ep_poll_callback)的时候,
* 不直接添加在ready list中,而是先暂时放在ovflist,
* 当ep_send_events_proc结束的时候,重新把ovflist中的数据加到ready list中。 */
struct epitem *ovflist;
/* The user that created the eventpoll descriptor */
/* 主要用来统计当前监听多少个fd。 */
struct user_struct *user;
/* epfd对应的struct file。 */
struct file *file;
/* used to optimize loop detection check */
/* 用于把一个epfd添加到另一个epoll中监听时候检测用。 */
int visited;
struct list_head visited_list_link;
};
epitem结构体及相应解释如下:
struct epitem {
/* RB tree node used to link this structure to the eventpoll RB tree */
/* 记录在struct eventpoll中的rbr节点。 */
struct rb_node rbn;
/* List header used to link this structure to the eventpoll ready list */
/* 记录在struct eventpoll中的rdllist节点。 */
struct list_head rdllink;
/*
* Works together "struct eventpoll"->ovflist in keeping the
* single linked chain of items.
*/
/* 记录在struct eventpoll中的ovflist节点,ovflist用处见eventpoll。 */
struct epitem *next;
/* The file descriptor information this item refers to */
/* 文件描述符和对应的file结构体的封装
* struct epoll_filefd {
* struct file *file;
* int fd;
* };
*/
struct epoll_filefd ffd;
/* Number of active wait queue attached to poll operations */
int nwait;
/* List containing poll wait queues */
/*
* struct eppoll_entry {
* /* List header used to link this structure to the "struct epitem" */
* struct list_head llink;
* /* The "base" pointer is set to the container "struct epitem" */
* struct epitem *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;
* };
* struct eppoll_entry节点,该节点在每个事件源在相应的设备中注册(ep_ptable_queue_proc)时候创建,
* 结构体中主要封装了当前事件源对应epitem,事件源在相应设备系统中的钩子和队列
* 当设备状态改变回调时,将通过eppoll_entry中的wait找到eppoll_entry结构体再
* 找到epitem(ep_item_from_wait)
*/
struct list_head pwqlist;
/* The "container" of this item */
/* 记录epitem所在的eventpoll。 */
struct eventpoll *ep;
/* List header used to link this item to the "struct file" items list */
/* struct file中的f_ep_links节点,好像是用作递归深度的检测,暂时没懂。 */
struct list_head fllink;
/* The structure that describe the interested events and the source fd */
/* 用户监听的事件类型。 */
struct epoll_event event;
};
3. EPOLL简单的运作流程:
epoll简单流程和reactor模式有一些相似,通过epoll来对事件源(用户关注的某个设备的某个状态)进行管理,每添加一个事件源,都会在对应设备上进行注册。当事件源有用户所关注的事件触发,就在中断回调时加入到epoll的ready list中,当用户epoll_wait的时候,等待超时时间(无事件触发),交出调度权,进程唤醒后如果有事件就将事件通知给用户,简单示意图如下:当然内在还有其他更多的细节处理,下文会描述。
4. epoll源码:
4.1 epoll_create ():
epoll_create将创建一个属于epoll文件系统的file,同时创建一个eventpoll的结构体,作为file的private_data,这里只注意eventpoll结构体初始化的函数ep_alloc中的ovflist,当其初始化为EP_UNACTIVE_PTR时,表示不使用该队列,当该队列开放使用时