epoll源码剖析
前提:Linux内核源码:linux-2.6.11.12
epoll是一个module,它的底层主要是通过一个文件统“eventpolls”来实现的;epoll的过程是通过三个函数来实现的:epoll_create()、epoll_ctl()、epoll_wait();
相关底层比较重要的结构体有struct eventpoll、struct epitem、struct epollentry、struct __wait_queue;
epoll_create()
epoll_create的底层是通过调用sys_epoll_create,这个系统调用实现的,这里是创建了一个文件系统(eventpoll),并把里面相关的节点关联起来(task_struct 、struct file_struct 、struct file 、struct dentry、struct inode 、struct eventpoll);
下面我们来进入到sys_epoll_create这个函数:
asmlinkage long sys_epoll_create(int size)
{
int error, fd;
struct inode *inode;
struct file *file;
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n",
current, size));
/* Sanity check on the size parameter */
error = -EINVAL;
if (size <= 0)
goto eexit_1;
/*
* Creates all the items needed to setup an eventpoll file. That is,
* a file structure, and inode and a free file descriptor.
*/
error = ep_getfd(&fd, &inode, &file);
if (error)
goto eexit_1;
/* Setup the file internal data structure ( "struct eventpoll" ) */
error = ep_file_init(file);
if (error)
goto eexit_2;
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
current, size, fd));
return fd;
eexit_2:
sys_close(fd);
eexit_1:
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
current, size, error));
return error;
}
<1>首先进入函数我们是进行了一系列的参数检查;
<2>接下来,ep_getfd(&fd, &inode, &file),这个函数它主要的作用是分配了fd,inode,file;节选ep_getfd中的重要函数:
file = get_empty_filp();/*pipefs文件系统中的管道分配一个索引节点 对象并对其进行初始化。*/
inode = ep_eventpoll_inode();//获取一个inode节点并对其进行初始化
error = get_unused_fd();//查找一个文件描述符
dentry = d_alloc(eventpoll_mnt->mnt_sb->s_root, &this);//分配一个struct dentry结构;
d_add(dentry, inode);//让struct dentry结构中的inode*指针指向inode节点;
fd_install(fd, file);//让files_struct里面的fd对应的下标为fd的成员指向file节点;
<3>函数ep_file_init(file),是分配和初始化了一个eventpoll节点,最后让file结构中的private_data指向这个eventpoll,让他们链接起来;
epoll_ctl()
epoll_ctl的底层实现是通过sys_epoll_ctl,它的作用主要是添加新的描述符,或者删除描述符,或者修改描述符所关心的事件;
这个系统调用实现的;以下为sys_poll_ctl中节选部分:
asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event)
{
file = fget(epfd);//通过epfd找到对应的文件对象的地址;
tfile = fget(fd);//通过fd找到对应的文件对象的地址;
ep = file->private_data;//获取file指向的eventpoll;
epi = ep_find(ep, tfile, fd);//在红黑树中查找有没有一个节点中的ffd中保存着tfile和fd,找到返回ffd所对应的epi,如果没有找到,就返回NULL;
switch (op) {
case EPOLL_CTL_ADD://添加
error = ep_insert(ep, &epds, tfile, fd);break;
case EPOLL_CTL_DEL://删除
error = ep_remove(ep, epi);break;
case EPOLL_CTL_MOD://修改关心的事件
error = ep_modify(ep, epi, &epds);break;
}
}
其中比较重要的函数为ep_insert,那么我们就分析一下ep_insert:
struct epitem *epi;
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
revents = tfile->f_op->poll(tfile, &epq.pt);//执行回调函数ep_ptable_queue_proc;
ep_rbtree_insert(ep, epi);//添加当前的文件到红黑树中;
ep_ptable_queue_proc()回调函数的作用:这个函数是在f_op->poll时被调用,该函数分配一个等待队列节点epoll_entry,它的作用是:1、把它挂载到文件系统的等待队列中;2、把它挂到epitem等待队列中;3、它还注册了一个等待队列的回调函数;
ep_poll_callback(),回调函数ep_poll_callback函数的作用:当文件操作完成,唤醒当前进程之前,会调用ep_poll_callback,把eventpoll放到epitem的完成队列中;
epoll_wait()
epoll_wait的底层实现是系统调用sys_epoll_wait,它的作用主要是等待文件操作完成并返回;
它的主体是ep_poll,该函数在for循环中,检查epitem上有没有事件就绪,如果有那么就返回,如果没有就调用schedule_timeout进入休眠,知道进程被再次唤醒;
asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events,
int maxevents, int timeout)
{
error = ep_poll(ep, events, maxevents, timeout);
}
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, long timeout)
{
for (;;) {
if (!list_empty(&ep->rdllist) || !jtimeout)
break;
jtimeout = schedule_timeout(jtimeout);
}
}
epoll性能优点:
epoll机制是通过select/poll的缺陷设计的,epoll是通过一个eventpolls文件系统,将为每个用户生成的fd直接拷贝到内核,不像select每次都得重新拷贝用户的描述符到内核;通过把操作拆分成epoll_create,epoll_ctl,epoll_wait三个函数来实现,避免了这个重复拷贝的过程;我们为每一个描述符所对应的等待队列节点,都设置了回调函数,只要有事件发生,那么由驱动程序去给我调用这个回调函数,将产生就绪事件的节点插入到rdlist,就绪事件链表中,这样的话,我们就可以以O(1)的方式,直接找到就绪事件,并返回给用户;
推荐阅读
-
浅析PHP7的多进程及实例源码
-
以Python的Pyspider为例剖析搜索引擎的网络爬虫实现方法
-
C语言实现的ls命令源码分享
-
建议收藏备用:.net core使用QRCoder生成普通二维码和带Logo的二维码详细使用教程,源码已更新至开源模板
-
netty源码解析(4.0)-28 ByteBuf内存池:PooledByteBufAllocator-把一切组装起来
-
微信小程序 自动登陆PHP源码实例(源码下载)
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
LZW压缩算法 C#源码
-
hashmap源码扩容(hashmap底层原理面试)