epoll剖析

参考学长博客和网上的资料 

http://blog.csdn.net/kongkongkkk/article/details/77418092

epoll

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

以上的话可以总结为几个方面

首先相比于select和poll而言epoll性能更为出众,并且cpu的利用率低。

并不需要遍历整个fd只有被事件唤醒的fd才需要便利,并且不需要每次重新从内核空间进行添加.。只需要第一次添加因为他是分开的,并不像select那样。其次它没有fd的限制,select默认1024;

好了首先先看一片知乎。先大体了解epoll及其工作原理。

golang数据结构内部实现 - linux下非阻塞io库 epoll(分享自知乎网)https://zhuanlan.zhihu.com/p/27050330?utm_source=qq&utm_medium=social

基本概念如下首先要用熟,几个api不进行介绍直接进行源码剖析。

struct epitem {
    /* RB tree node used to link this structure to the eventpoll RB tree */
    //每个epitem都存放在eventpoll中以rbr为根的红黑树中
    //rbn记录epitem在红黑树中的结点
    struct rb_node rbn;

    /* List header used to link this structure to the eventpoll ready list */
    //每个就绪事件所对应的epitem都链入了eventpoll中的rdllink
    //rdllink记录就绪链表头
    struct list_head rdllink;

    /*
     * Works together "struct eventpoll"->ovflist in keeping the
     * single linked chain of items.
     */
    //记录每个epitem在eventpoll数据结构中的ovflist的下一个epitem
    struct epitem *next;

    /* The file descriptor information this item refers to */
    //epoll_filefd数据结构记录epitem所对应的struct file和fd文件描述符
    struct epoll_filefd ffd;

    /* Number of active wait queue attached to poll operations */
    //poll操作上的等待队列个数
    int nwait;

    /* List containing poll wait queues */
    //包含等待队列对头的单链表
    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 */
    //记录epitem所对应的struct file的单链表
    struct list_head fllink;

    /* The structure that describe the interested events and the source fd */
    //记录epitem对应的epoll_event数据结构,epoll_event是epoll_ctl函数传入的参数 
    struct epoll_event event;
};
struct eventpoll {  
    spinlock_t lock;  
    struct mutex mtx;  
    wait_queue_head_t wq; // sys_epoll_wait() 等待在这里  
    // f_op->poll()  使用的, 被其他事件通知机制利用的wait_address  
    wait_queue_head_t poll_wait;  
    /* 已就绪的需要检查的epitem 列表 */  
    struct list_head rdllist;  
    /* 保存所有加入到当前epoll的文件对应的epitem*/  
    struct rb_root rbr;  
    // 当正在向用户空间复制数据时, 产生的可用文件  
    struct epitem *ovflist;  
    /* The user that created the eventpoll descriptor */  
    struct user_struct *user;  
    struct file *file;  
    /*优化循环检查,避免循环检查中重复的遍历 */  
    int visited;  
    struct list_head visited_list_link;  
}  
truct epoll_filefd {  
    struct file *file;  
    int fd;  
};  
  
// 与一个文件上的一个wait_queue_head 相关联,因为同一文件可能有多个等待的事件,这些事件可能使用不同的等待队列  
struct eppoll_entry {  
    // List struct epitem.pwqlist  
    struct list_head llink;  
    // 所有者  
    struct epitem *base;  
    // 添加到wait_queue 中的节点  
    wait_queue_t wait;  
    // 文件wait_queue 头  
    wait_queue_head_t *whead;  
};  
  
// 用户使用的epoll_event  
struct epoll_event {  
    __u32 events;  
    __u64 data;  
} EPOLL_PACKED;
对与epoll所有api而言主要是对这五个结构体的一系列操作。

对于epoll_create函数每个epollfd都会对应一个叫做eventpoll 事件池的东西,执行epoll_create 最主要的是创建了就绪链表和红黑树,采用红黑树结构的好处是查找效率高并且增加和删除的效率也不错相比于其他数据结构而言。

还有一个很重要的东西struct file 变量他的作用是:

eventpoll中维持了一个 结构体  epoll向内核注册文件系统用于存储监控socket调用epoll_create虚拟epoll文件系统创建file结点file普通文件服务于epollepoll内核初始化(操作系统启epoll自内核高速cache区用于安置每想监控socket些socket红黑树形式保存内核cache支持快速查找、插入、删除内核高速cache区建立连续物理内存页建立slab层简单物理想要size内存使用使用空闲象。所谓返回的fd并不是对应真正的文件他只是对应了一个虚拟的struct file。epoll独立的文件管理系统,添加一些对struct file的一些操作。

调用epoll_ctl往塞入百万句柄epoll_wait仍飞快事件句柄给我用户由于我调用epoll_create内核除帮我epoll文件系统file结点内核cache红黑树用于存储epoll_ctl传socket外再建立list链表用于存储准备事件epoll_wait调用仅仅观察list链表数据即数据数据sleeptimeout即使链表没数据epoll_wait非高效。(访问上的高效)

SYSCALL_DEFINE1(epoll_create1, int, flags)
{
    int error;
    //eventpoll是epoll中非常重要的数据结构!每一个epollfd都有一个对应的eventpoll数据结构
    //eventpoll数据结构定义在上面
    struct eventpoll *ep = NULL;

    /* Check the EPOLL_* constant for consistency.  */
    BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);

    if (flags & ~EPOLL_CLOEXEC)
        return -EINVAL;
    /*
     * Create the internal data structure ("struct eventpoll").
     */
    //初始化一个eventpoll数据结构
    //ep_alloc定义在下面
    error = ep_alloc(&ep);
    if (error < 0)
        return error;
    /*
     * Creates all the items needed to setup an eventpoll file. That is,
     * a file structure and a free file descriptor.
     */
    //创建epollfd
    //因为epollfd并不存在真正对应的文件,所以内核创建了一个虚拟的文件,并为这个虚拟文件分配struct file数据结构
    //参数eventpoll_fops就是file operations,即文件支持的操作
    //关于file operations在之前的poll机制内核源代码剖析一文中已经做了非常深入的解释
    //这里简单解释一下,file operations中的每一个成员都是回调函数指针,对应每一种操作的具体实现
    //epollfd文件实现了三种操作,即release、poll、llseek
    //eventpoll_fops数据结构定义在下面
    //参数ep就是epollfd所对应的eventpoll数据结构,在anon_inode_getfd中,将struct file的private_data成员赋值为ep的地址
    //anon_inode_getfd定义在下面
    error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,
                 O_RDWR | (flags & O_CLOEXEC));
    if (error < 0)
        ep_free(ep);
    //返回epollfd的值
    return error;
}
接下来主要看一下这个东西(epoll_create具体分配的东西也就是event_poll 结构体中的东西)

static int ep_alloc(struct eventpoll **pep)
{
    int error;
    struct user_struct *user;
    struct eventpoll *ep;

    //获取当前用户信息
    user = get_current_user();
    error = -ENOMEM;
    //通过kmalloc为eventpoll数据结构分配内存空间
    ep = kzalloc(sizeof(*ep), GFP_KERNEL);
    if (unlikely(!ep))
        goto free_uid;

    spin_lock_init(&ep->lock);
    mutex_init(&ep->mtx);//对互斥锁进行初始化
    //初始化eventpoll中的wq
    init_waitqueue_head(&ep->wq);
    //初始化eventpoll中的poll_wait
    init_waitqueue_head(&ep->poll_wait);
    //初始化存放就绪事件所对应的epitem的双向链表
    INIT_LIST_HEAD(&ep->rdllist);
    //初始化存放所有事件对应的epiitem的红黑树,初始值为NULL
    //#define RB_ROOT   (struct rb_root) { NULL, }
    ep->rbr = RB_ROOT;
    //初始化转移到用户空间之前,存放就绪事件所对应的epitem的双向链表,初始值为-1L
    //#define EP_UNACTIVE_PTR ((void *) -1L)
    ep->ovflist = EP_UNACTIVE_PTR;
    //初始化用户信息
    ep->user = user;
    //为eventpoll数据结构指针赋值
    *pep = ep;

    return 0;

free_uid:
    free_uid(user);
    return error;
}

epollfd和event_poll 的绑定操作代码如下

//由此可见epollfd所对应的的匿名文件只实现了三种操作
//release操作为释放epollfd所对应的eventpoll数据结构
//ep_eventpoll_release定义在下面
//poll操作为事件就绪时,调用poll操作对应的回调函数对当前进程进行一些列操作
//ep_eventpoll_poll定义先放一边,在epoll_wait中会详细解释
//llseek操作为获取匿名文件的游标偏移
//noop_llseek定义在下面
static const struct file_operations eventpoll_fops = {
    .release    = ep_eventpoll_release, 
    .poll       = ep_eventpoll_poll,
    .llseek     = noop_llseek,
};
static int ep_eventpoll_release(struct inode *inode, struct file *file)
{
    //通过struct file中的成员private_data得到epollfd所对应的eventpoll数据结构
    struct eventpoll *ep = file->private_data;
    //释放eventpoll数据结构
    if (ep)
        ep_free(ep);

    return 0;
}
1
int anon_inode_getfd(const char *name, const struct file_operations *fops,
             void *priv, int flags)
{
    int error, fd;
    struct file *file;
    //分配文件描述符,即epollfd
    error = get_unused_fd_flags(flags);
    if (error < 0)
        return error;
    fd = error;
    //创建匿名文件
    file = anon_inode_getfile(name, fops, priv, flags);
    if (IS_ERR(file)) {
        error = PTR_ERR(file);
        goto err_put_unused_fd;
    }
    //将文件描述符fd和匿名文件绑定,即将file_struct中的fdtable的成员fd[fd]赋值为file
    fd_install(fd, file);
    //返回epollfd的值
    return fd;

err_put_unused_fd:
    put_unused_fd(fd);
    return error;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值