epoll源码剖析

来源:互联网 发布:我的世界商店js 编辑:程序博客网 时间:2024/06/12 22:57

前提:Linux内核源码:linux-2.6.11.12

这里写图片描述

epoll是一个module,它的底层主要是通过一个文件统“eventpolls”来实现的;epoll的过程是通过三个函数来实现的:epoll_create()、epoll_ctl()、epoll_wait();
相关底层比较重要的结构体有struct eventpollstruct epitemstruct epollentrystruct __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)的方式,直接找到就绪事件,并返回给用户;

原创粉丝点击