对于epoll实现原理的理解

68 篇文章 1 订阅
19 篇文章 1 订阅
本文详细探讨了epoll在Linux中的实现原理,包括epoll的数据结构、锁的机制、回调函数以及LT与ET的工作模式。通过对epoll的深入理解,阐述了其在处理IO多路复用时的优势和常见误区。
摘要由CSDN通过智能技术生成

Epoll是Linux IO的多路复用的机制,是select/poll的增强版本,在Linux内核fs/eventpoll.c中可以查看epoll的具体的实现。

一、epoll数据结构

  学习任何组件,首先得知道它有什么数据结构或者数据类型,epoll主要有两个结构体:eventpoll和epitem。epitem是每一个IO对应的事件,比如EPOLL_CTL_ADD操作时,就需要创建一个epitem;eventpoll是每一个epoll所对应的,比如epoll_create就是创建一个eventpoll。

struct epitem {
    union {
        /* RB tree node links this structure to the eventpoll RB tree */
        struct rb_node rbn;
        /* Used to free the struct epitem */
        struct rcu_head rcu;
    };

    /* List header used to link this structure to the eventpoll ready list */
    struct list_head rdllink;

    /*
     * Works together "struct eventpoll"->ovflist in keeping the
     * single linked chain of items.
     */
    struct epitem *next;

    /* The file descriptor information this item refers to */
    struct epoll_filefd ffd;//sockfd

    /* List containing poll wait queues */
    struct eppoll_entry *pwqlist;

    /* The "container" of this item */
    struct eventpoll *ep;

    /* List header used to link this item to the "struct file" items list */
    struct hlist_node fllink;

    /* wakeup_source used when EPOLLWAKEUP is set */
    struct wakeup_source __rcu *ws;

    /* The structure that describe the interested events and the source fd */
    struct epoll_event event;
};

struct eventpoll {

    struct mutex mtx;

    /* 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;

    /* Lock which protects rdllist and ovflist */
    rwlock_t lock;

    /* RB tree root used to store monitored fd structs */
    struct rb_root_cached rbr;

    /*
     * This is a single linked list that chains all the "struct epitem" that
     * happened while transferring ready events to userspace w/out
     * holding ->lock.
     */
    struct epitem *ovflist;

    /* wakeup_source used when ep_scan_ready_list is running */
    struct wakeup_source *ws;

    /* The user that created the eventpoll descriptor */
    struct user_struct *user;

    struct file *file;

    /* used to optimize loop detection check */
    u64 gen;
    struct hlist_head refs;

#ifdef CONFIG_NET_RX_BUSY_POLL
    /* used to track busy poll napi_id */
    unsigned int napi_id;
#endif

#ifdef CONFIG_DEBUG_LOCK_ALLOC
    /* tracks wakeup nests for lockdep validation */
    u8 nests;
#endif
};

 数据结构如下图所示。

  list用来存储就绪的IO,rbtree用来存储所有IO,方便快速查找fd,这两种数据结构我们都从inster和remove来讨论。对于list,当内核IO准备就绪时,则执行epoll_event_callback的回调函数,将epitem添加到list中;当epoll_wait激活重新运行时,将list的epitem 逐一拷贝到events中,并删除list中被拷贝出来的epitem。

对于rbtree又该何时添加何时删除呢?当app执行epoll_ctl(EPOLL_CTL_ADD)操作,将epitem添加到rbtree中;当app执行epoll_ctl(EPOLL_CTL_DEL)操作,将对应的epitem从rbtree中删除。那么list和rbtree又如何做到线程安全呢?

二、epoll锁的机制

  list使用最小粒度的锁spinlock,便于在SMP下添加操作的时候,能够快速操作list。避免SMP体系下,多核竞争,此处采用自旋锁,不适合采用睡眠锁;添加操作如下。

    (1)获取spinlock

    (2)epitem的rdy置为1,代表epitem已在就绪队列中

    (3)添加到list

    (4)将eventpoll的rdnum加1

    (5)释放spinlock

  删除则与添加类似。

  对于rbtree的操作使用互斥锁,过程如下:

    (1)获取互斥锁

    (2)查找sockid的epitem是否存在,不存在可以添加

    (3)分配epitem

    (4)sockid赋值

    (5)设置event添加到epitem的event域

    (6)将epitem添加到rbtree

    (7)释放互斥锁

  这里的互斥锁,锁的是整颗树,而不是节点;删除则与之类似操作。

三、epoll回调

  首先要知道回调函数何时执行,此部分需要与tcp的协议栈联系起来理解。

  (1)三次握手完成时,把fd加入到就绪队列,把event置为EPOLLIN可读,此时标识可以进入到accept读取socket数据;

  (2)recvbuffer有数据的时候(可读数据),找到对应的fd加入到就绪队列,把event置EPOLLIN为可读;

  (3)sendbuffer有空隙的时候(可发数据),找到对应的fd加入到就绪队列,把event置EPOLLOUT为可写;

  (4)接收到fin的时候(断开连接),找到对应的fd加入到就绪队列,把event置EPOLLIN为可读;

四、LT与ET

  (1)LT是水平出发,有数据就一直触发,只要recvBuffer里面有数据就一直触发,直到数据读取完,适用于大块;

  (2)ET是边沿触发,从没有数据到有数据,才触发,只触发一次,即使没读完数据,也不会再触发去读取剩余的数据,剩余的数据等待下一次触发再读,适用于小块;

思考:

  就绪集合为啥使用队列而不适用栈?

    首先就绪集合的数据本身就需要遍历所有,肯定使用链式的数据结构,如果使用栈,就会存在就绪节点一次那不完的情况,导致上一次没被取出的节点,在下一次epoll_wait再拿的时候也可能拿不到,导致出现一些就绪节点永远都不被处理。

  epoll的误区:

  (1)epoll性能高,里面有内存映射,mmap

  (2)epoll比select/poll要高,在fd很少时select/poll比epoll更好

C/C++Linux服务器开发高级架构师/C++后台开发架构师​免费学习地址

另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,详情看以下视频

Linux C/C++后台开发学习资源整理,包括教学视频,文档,面试题,学习路线图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值