百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

你真的了解epoll吗?深入epoll的五个问题

liebian365 2024-10-27 13:12 7 浏览 0 评论

由于epoll用的比较多,最近看到一些网友关于epoll的问答,所以我就想整理成一篇文章,这样看起来和理解起来都方便一些。

?问题1:什么是epoll的ET/LT模式,select/poll支持吗?

ET是edge trigger,也就是边缘触发,它是指当可读/可写事件到来时触发,重点是它只触发一次,见下图:

LT是level trigger,也就是水平触发,它是重点是可以触发多次,

代码实现实际上很简单,只有一行代码,没有想到吧。

do_epoll_wait
|--ep_poll
    |--ep_send_events
        |--ep_send_events_proc
 static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
             void *priv)
{
    ....
    list_for_each_entry_safe(epi, tmp, head, rdllink) {
    ....
    if (epi->event.events & EPOLLONESHOT)
      epi->event.events &= EP_PRIVATE_BITS;
    //就是这句,如果不是ET,就把epi重新加入ep的就绪列表中,
    //这样下次再调用epoll_wait时,还能再就绪列表中看到这个epi,
    //然后再看看该epi是否可读。
    else if (!(epi->event.events & EPOLLET)) {
      /*
       * If this file has been added with Level
       * Trigger mode, we need to insert back inside
       * the ready list, so that the next call to
       * epoll_wait() will check again the events
       * availability. At this point, no one can insert
       * into ep->rdllist besides us. The epoll_ctl()
       * callers are locked out by
       * ep_scan_ready_list() holding "mtx" and the
       * poll callback will queue them in ep->ovflist.
       */
      list_add_tail(&epi->rdllink, &ep->rdllist);
      ep_pm_stay_awake(epi);
    }

问题2:为啥我们总建议ET触发模式跟non-block的socket一起使用呢?那能不能跟block的socket一起用呢?

  • 由于ET仅触发一次,所以很多资料都建议说ET要跟non-block的socket一起使用,很多也都没有解释原因,我来解答一下:

首先ET模式可以跟block的socket使用,没有问题,可以工作,原因是epoll只是一种让用户态知道socket是可读可写的方式或方法,epoll本身并不能影响socket本身的读写。为啥用户态要知道socket是否可读可写呢?原因是假如我们有1000个socket,每次只有两个socket是可读/可写的,这个时候我们肯定不希望遍历1000个socket,都去内核使用read/write去尝试的,我们希望内核能一次性告诉我们哪两个socket是可读/可写的。

  • 那为啥建议ET跟non-block配合使用呢?

因为有ET的特性,当告诉我们socket可读时,我们要一直读,读到没有数据为止,可是如果是block的socket的话,这个时候就会阻塞再read中,导致我们不能等待再epoll_wait中:

//伪码
while(true){
    struct epoll_event *events;
    events = (struct epoll_event *) calloc (MAXEVENTS, sizeof event);
    //如果fd是非阻塞的,就会阻塞再epoll_wait中
    int n = epoll_wait(ep, events,...);
    for(int i=0; i<n; i++){
        //如果fd是阻塞的,就会阻塞再read中
        while(read(events[i].data.fd,...) >);
    }    
}

问题3:我们能不能把一个句柄(socket)添加到不同的epoll_event中?

可以的,甚至可以把socket添加到epoll_event后,还可以使用select/poll,原因是因为sock使用一个列表来管理等待唤醒列表,当然我们可以添加多个。

|--tcp_poll
   |--sock_poll_wait
static inline void sock_poll_wait(struct file *filp, struct socket *sock,
          poll_table *p)
{
  if (!poll_does_not_wait(p)) {
     //sock->wq.wait是队列头,可以添加多个订阅者着
    poll_wait(filp, &sock->wq.wait, p);
    /* We need to be sure we are in sync with the
     * socket flags modification.
     *
     * This memory barrier is paired in the wq_has_sleeper.
     */
    smp_mb();
  }
}

我们可以构造一种使用场景,client两个线程,一个读,而另一个写同一个socket,server侧也有两个线程,同样是一个读一个写这个socket,拓扑如下:

相关视频推荐

支撑亿级io的底层基石-epoll实战揭秘

6种epoll的设计,学会了吊打面试官

手把手带你实现epoll组件,为tcp并发的实现epoll

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

问题4:EPOLLONESHOT时,如果缓冲区有未读取的数据,会立刻触发事件么?

首先我们得知道什么是EPOLLONESHOT,顾名思义就是一次性,这个一次性是指我只接收一次该fd的可读/可写等通知,后续有的话也不要通知我了,如果我再想要得到通知的话,就再使用:epoll_ctl (efd, EPOLL_CTL_ADD, fd, &event) 来添加一下。

do_epoll_wait
|--ep_poll
    |--ep_send_events
        |--ep_send_events_proc
 static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
             void *priv)
{
    ....
    list_for_each_entry_safe(epi, tmp, head, rdllink) {
    ....
    if (epi->event.events & EPOLLONESHOT)
      //清掉EPOLLIN EPOLLOUT EPOLLERR等event
      epi->event.events &= EP_PRIVATE_BITS;

这个时候就有个问题,那就是等我调用epoll_ctl (efd, EPOLL_CTL_ADD, fd, &event)来把socket的fd添加到ep中时,那如果这个时候该socket是可读/可写的,后面我epoll_wait时能通知到我吗?

答案是:可以的。

我们来解释一下原因,

1)重设EPOLLONESHOT在前,数据到来在后,这没有问题,因为重设EPOLLONESHOT时,同时app要带原有的event标志,等数据来时,就可以放入就绪列表中了

2) 数据到来在前,重设EPOLLONESHOT在后,在重设EPOLLONESHOT时,设置完event标志后,内核会做一次poll动作,如果是就绪状态(可读、可写等),内核就把这个fd放入就绪列表中。当app重设EPOLLONESHOT时,内核会调用到下面函数:

fs/eventpoll.c
static int ep_modify(struct eventpoll *ep, struct epitem *epi,
         const struct epoll_event *event)
{
    ...
     epi->event.events = event->events; /* need barrier below */
  epi->event.data = event->data; /* protected by mtx */
        ...
                 //执行poll,
          if (ep_item_poll(epi, &pt, 1)) {
    write_lock_irq(&ep->lock);
                //如果可用且没有在就绪列表,就放入就绪列表
    if (!ep_is_linked(epi)) {
      list_add_tail(&epi->rdllink, &ep->rdllist);

le data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">

这个是可以的,我们不要忘了epoll是做什么的,epoll是实现多路IO通知机制的,比如我有1000个socket,我得知道哪些是可读/可写的,然后我再去读/写它,比如下面的伪码:

while(1)
{
    int n = epoll_wait (efd, events, MAXEVENTS, -1);
    //精确知道哪个可读可写
    for(int i=0; i<n;i++)
    {
        read(events[i].data.fd,...);
    }
}

然而如果我们不使用epoll这样的通知机制我们要怎么做呢?

实际上我们也可以做,只是效率低下:

while(1)
{
    for(int i=0; i<n;i++) //不知道哪个socket是可读/可写,我们
                           //全部遍历
    {
        read(socket[i],...); //socket可以是block和non-block
    }
    sleep(1);//休息一会,避免当socket全是non-block时,持续占CPU
}

所以即使你把fd添加到epoll/select/poll中,不会影响fd的可读可写的。

总结:

相比select/poll来说epoll是比较的高效,这跟它的实现是有关的,然而select/poll的实现是简单的,目的都是相同的,都是为了实现多路IO复用,然而它们不会影响到fd(包括socket)的读写,任何使用你都可以使用write/read去读写数据的,只是你如果想在读写之前知道该fd是否可读/可写的话,就要使用select/poll/epoll。

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: