精华内容
下载资源
问答
  • linux中 epoll poll 和select区别
  • epoll和select区别

    万次阅读 多人点赞 2012-05-16 21:38:44
    先说下本文框架,先是问题引出,然后概括两个机制的区别和联系,最后介绍每个接口的用法 一、问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述...

    先说下本文框架,先是问题引出,然后概括两个机制的区别和联系,最后介绍每个接口的用法

    一、问题引出 联系区别

    问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读出来,这样实时性不能满足要求,大概的解决方案有以下几种:

    1.使用多进程或者多线程,但是这种方法会造成程序的复杂,而且对与进程与线程的创建维护也需要很多的开销。(Apache服务器是用的子进程的方式,优点可以隔离用户)

    2.用一个进程,但是使用非阻塞的I/O读取数据,当一个I/O不可读的时候立刻返回,检查下一个是否可读,这种形式的循环为轮询(polling),这种方法比较浪费CPU时间,因为大多数时间是不可读,但是仍花费时间不断反复执行read系统调用。

    3.异步I/O(asynchronous I/O),当一个描述符准备好的时候用一个信号告诉进程,但是由于信号个数有限,多个描述符时不适用。

    4.一种较好的方式为I/O多路转接(I/O multiplexing)(貌似也翻译多路复用),先构造一张有关描述符的列表(epoll中为队列),然后调用一个函数,直到这些描述符中的一个准备好时才返回,返回时告诉进程哪些I/O就绪。select和epoll这两个机制都是多路I/O机制的解决方案,select为POSIX标准中的,而epoll为Linux所特有的。

    区别(epoll相对select优点)主要有三:

    1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE    1024  表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。

    2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。

    3.使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。

    二、接口

    1)select

    1. int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);

    struct timeval{

      long tv_sec;

      long tv_usec;

    }

    有三种情况:tvptr == NULL 永远等待;tvptr->tv_sec == 0 && tvptr->tv_usec == 0 完全不等待;不等于0的时候为等待的时间。select的三个指针都可以为空,这时候select提供了一种比sleep更精确的定时器。注意select的第一个参数maxfdp1并不是描述符的个数,而是最大的描述符加1,一是起限制作用,防止出错,二来可以给内核轮询的时候提供一个上届,提高效率。select返回-1表示出错,0表示超时,返回正值是所有的已经准备好的描述符个数(同一个描述符如果读和写都准备好,对结果影响是+2)。

    2.int FD_ISSET(int fd, fd_set *fdset);  fd在描述符集合中非0,否则返回0

    3.int FD_CLR(int fd, fd_set *fd_set); int FD_SET(int fd, fd_set *fdset) ;int FD_ZERO(fd_set *fdset);

    用一段linux 中man里的话“FD_ZERO()  clears  a set.FD_SET() and  FD_CLR() respectively add and remove a given file descriptor from a set.  FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.”这几个函数与描述符的0和1没关系,只是添加删除检测描述符是否在set中。

    2)epoll

    1.int epoll_create(int size);
    创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

    2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;
    第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
    struct epoll_event {
      __uint32_t events;  /* Epoll events */
      epoll_data_t data;  /* User data variable */
    };

    events可以是以下几个宏的集合:
    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

    关于epoll工作模式ET,LT

    LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
    ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了,但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)

    3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
    等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

    三、参考:

    APUE(I/O多路转接)

    linux man epoll select

    http://blog.chinaunix.net/uid-22663647-id-1771846.html 

    http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html

    展开全文
  • 关于epoll和select区别

    千次阅读 2019-10-18 09:56:28
    问题:关于 epoll select区别,哪些说法是正确的?(多选) A. epoll select 都是 I/O 多路复用的技术,都可以实现同时监听多个 I/O 事件的状态。 B. epoll 相比 select 效率更高,主要是基于其操作系统...

    问题:关于 epoll 和 select 的区别,哪些说法是正确的?(多选)

    A. epoll 和 select 都是 I/O 多路复用的技术,都可以实现同时监听多个 I/O 事件的状态。

    B. epoll 相比 select 效率更高,主要是基于其操作系统支持的I/O事件通知机制,而 select 是基于轮询机制。

    C. epoll 支持水平触发和边沿触发两种模式。

    D. select 能并行支持 I/O 比较小,且无法修改。

    出题人:阿里巴巴出题专家:寈峰/阿里技术专家

    参考答案:A,B,C

    【延伸】那在高并发的访问下,epoll使用那一种触发方式要高效些?当使用边缘触发的时候要注意些什么东西,下面将详细讲解epoll

    • epoll是什么

      • epoll 是Linux下多路复用IO接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集用来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

      • epoll除了提供 select/poll 那种IO事件的电平触发 (Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少 epoll_wait/epoll_pwait 的调用,提高应用程序效率。

    epoll函数

    • 说明:epoll对IO接口进行多路复用,提高CPU的利用率

    • 头文件:#include <sys/epoll.h>

    • 函数1:int epoll_create1(int flags);(向内核注册并打开一个 epoll 描述符)

      • 这个函数是在linux 2.6.27中加入的,它和 epoll_create 差不多,不同的是 epoll_create1 函数的参数是 flag,当 flag 是 0 时,表示和 epoll_create 函数完全一样,不需要 size 的提示了。

        • 当 flag = EPOLL_CLOEXEC,创建的epfd会设置FD_CLOEXEC(一般使用这种)

          • FD_CLOEXEC:它是 fd 的一个标识说明,用来设置文件 close-on-exec 状态的。当 close-on-exec 状态为 0 时,调用 exec 时,fd 不会被关闭;状态非零时则会被关闭,这样做可以防止 fd 泄露给执行 exec 后的进程。
        • 当 flag = EPOLL_NONBLOCK,创建的 epfd 会设置为非阻塞

      • int epoll_create(int size);

        • 创建一个 epoll 的句柄,size用来告诉内核这个监听的数目最大值。

        • 当创建好 epoll 句柄后,它就是会占用一个 fd 值,所以在使用完 epoll 后,必须调用 close() 关闭,否则可能导致 fd 被耗尽。

        • 注意:是数量的最大值,不是 fd 的最大值(通知内核需要监听 size 个fd)。

        • 自从Linux2.6.8版本以后,size值其实是没什么用的,不过要大于0,因为内核可以动态的分配大小,所以不需要 size 这个提示了。

      • 为什么使用 epoll_create1 函数而不用以前的 epoll_create 函数:

        • epoll_create 的参数 size 是老版本的实现,使用的是 hash 表, size 应该是用来算 bucket 数目,后面因为使用红黑树,这个参数不再使用, 可以忽略。
      • 返回值:成功返回一个非负的文件描述符,出错返回-1,并设置errno:

        • EINVAL : 无效的标志
        • EMFILE : 用户打开的文件超过了限制
        • ENFILE : 系统打开的文件超过了限制
        • ENOMEM : 没有足够的内存完成当前操作
    • 函数2:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);(epoll 的事件注册函数)

      • epfd参数:epoll_create1 函数返回的的 epoll 描述符

      • op参数:操作值(想要注册的动作)

        • EPOLL_CTL_ADD: 注册目标fd到epfd中,同时关联内部event到fd上
        • EPOLL_CTL_MOD: 修改已经注册到fd的监听事件
        • EPOLL_CTL_DEL: 从epfd中删除/移除已注册的fd,event可以被忽略,也可以为NULL
      • fd参数:需要监听的套接字描述符

      • event参数:设定监听事件的结构体

        • typedef union epoll_data {
              void        *ptr;
              int          fd;
              __uint32_t   u32;
              __uint64_t   u64;
          } epoll_data_t;
          
          struct epoll_event {
              __uint32_t   events; /* Epoll events */
              epoll_data_t data;   /* User data variable */
          };

          data是一个联合体,能够存储fd或其它数据,我们需要根据自己的需求定制。events表示监控的事件的集合,是一个状态值,通过状态位来表示,可以设置如下事件:

          • EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
          • EPOLLOUT:表示对应的文件描述符可以写;
          • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
          • EPOLLERR:表示对应的文件描述符发生错误;
          • EPOLLHUP:表示对应的文件描述符被挂断;(并不代表对端结束了连接,通常情况下 EPOLLHUP 表示的是本端挂断)
          • EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
          • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,就会把这个fd从epoll的队列中删除。如果还需要继续监听这个socket的话,需要再次把这个fd加入到EPOLL队列里
      • 返回值:成功返回0,失败返回-1,并设置errno:

        • EBADF : epfd 或者 fd 不是一个有效的文件描述符
        • EEXIST : op 为 EPOLL_CTL_ADD,但 fd 已经被监控
        • EINVAL : epfd 是无效的 epoll 文件描述符
        • ENOENT : op 为 EPOLL_CTL_MOD 或者 EPOLL_CTL_DEL,并且 fd 未被监控
        • ENOMEM : 没有足够的内存完成当前操作
        • ENOSPC : epoll 实例超过了 /proc/sys/fs/epoll/max_user_watches 中限制的监听数量
    • 函数3:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);

      • epfd参数:epoll 的描述符( epoll_create1 函数的返回值)

      • events参数:events 指针携带有 epoll_data_t 数据

      • maxevents参数:告诉内核 events 有多大,该值必须大于0,但是这个 maxevents 的值不能大于创建 epoll_create() 时的 size

      • timeout参数(毫秒):表示超时时间。0 表示不等待立即返回,-1 代表永久阻塞,大于 0 表示等待的最大时间

      • epoll_pwait(since linux 2.6.19)允许一个应用程序安全的等待,直到 fd 设备准备就绪,或者捕获到一个信号量。其中 sigmask 表示要捕获的信号量。

      • 返回值:成功返回准备好事件要求的描述符的个数,返回0代表在超时时间内没有准备好的描述符;失败返回 -1 并设置 errno:

        • EBADF:epfd 不是一个有效的描述符
        • EFAULT:events 指向的内存区域没有写的权限
        • EINTR:在事件发生任何请求或超时过期之前,调用被信号处理程序中断
        • EINVAL:epfd 不是一个 epoll 描述符,或者 maxevents 参数小于或等于 0
    • 示例:

      这里使用伪代码,具体实现的代码请移步

      epfd = epoll_create1(EPOLL_CLOEXEC);
      event.events = EPOLLET | EPOLLIN;
      event.data.fd = serverfd;
      epoll_ctl(epfd, EPOLL_CTL_ADD, serverfd, &event);
      // 主循环
      while(true) {
          // 这里的timeout很重要,实际使用中灵活调整
          count = epoll_wait(epfd, &events, MAXEVENTS, timeout);
          for(i = 0; i < count; ++i) {
              if(events[i].events & EPOLLERR || events[i].events & EPOLLHUP)
                  // 处理错误
              if(events[i].data.fd == serverfd)
                  // 为接入的连接注册事件
              else if(events[i].events & EPOLLIN)
                  // 处理可读的缓冲区
                  read(events[i].data.fd, buf, len);
                  event.events = EPOLLET | EPOLLOUT;
                  event.data.fd = events[i].data.fd;
                  epoll_ctl(epfd, EPOLL_CTL_MOD, events[i].data.fd, &event);
              else
                  // 处理可写的缓冲区
                  write(events[i].data.fd, buf, len);
                  // 后续可以关闭fd或者MOD至EPOLLOUT
          }
      }

    说明

    • 为什么用epoll

      • 支持监听大数目的socket描述符

        • 一个进程内,select能打开的fd是有限制的,由宏FD_SETSIZE 设置,默认值是 1024.在某些时候,这个数值是远远不够用的。解决办法有两种,一是修改宏然后重新编译内核,但与此同时会引起网络效率的下降;二是使用多进程来解决,但是创建多个进程是有代价的,而且进程间数据同步没有多线程间方便。

        • epoll没有这个限制,它所支持的最大 fd 上限远远大于1024,在1GB内存的机器上是10万左右(具体数目可以cat/proc/sys/fs/file-max查看)

          //我的是centos6.9,内存2GB
          [roux@Vkey mnt]$ cat /proc/sys/fs/file-max 
          187510
          [roux@Vkey mnt]$
      1. 效率的提高

        • select 函数每次都当监听的套接组有事件产生时就会返回,但却不能将有事件产生的套接字筛选出来,而是改变其在套接组的标志量,所以每次监听到事件,都需要将套接组整个遍历一遍。时间复杂度是O(n)。当fd数目增加时,效率会线性下降。

        • epoll 函数每次会将监听套结字中产生事件的套接字加到一列表中,然后我们可以直接对此列表进行操作,而没有产生事件的套接字会被过滤掉,极大的提高了IO效率。这一点尤其在套接字监听数量巨大而活跃数量很少的时候很明显。

      2. 内存处理

        • 不管是哪种I/O机制,都无法避免fd在操作过程中拷贝的问题,而 epoll 使用了 mmap (是指文件/对象的内存映射,被映射到多个内存页上),所以同一块内存(共享内存)就可以避免这个问题(详细看前面的零拷贝技术及 mmap)。
    • epoll的工作方式

      • epoll有两种触发模式,LT(Level-Triggered,水平触发),ET(Edge–Triggered边缘触发)。其中LT编程模型和poll是一样的。

      • LT(level triggered) 是默认/缺省的工作方式,同时支持 block 和 no_block socket。这种工作方式下,内核会通知你一个 fd 是否就绪,然后才可以对这个就绪的 fd 进行I/O操作。就算你没有任何操作,系统还是会继续提示 fd 已经就绪,不过这种工作方式出错会比较小,传统的 select/poll 就是这种工作方式的代表。(水平触发,只要有数据可以读,不管怎样都会通知)

      • ET(edge-triggered) 是高速工作方式 仅支持 no_block socket(非阻塞),这种工作方式下,当 fd 从未就绪变为就绪时,内核会通知 fd 已经就绪,并且内核认为你知道该 fd 已经就绪,不会再次通知了,除非因为某些操作导致fd就绪状态发生变化。如果一直不对这个 fd 进行I/O操作,导致 fd 变为未就绪时,内核同样不会发送更多的通知,因为 only once。所以这种方式下,出错率比较高,需要增加一些检测程序。(边缘触发,只有状态发生变化时才会通知,可以理解为电平变化)

      • 举例:

        • LT 水平触发

          儿子:“妈妈,我收到了5000元压岁钱。”
          妈妈:“恩,省着点花!”
          儿子:“妈妈,我今天买了个ipad,花了3000元。”
          妈妈:“噢,这东西真贵。”
          儿子:“妈妈,我今天买好多吃的,还剩1000元。”
          妈妈:“用完了这些钱,我可不会再给你了。”
          儿子:“妈妈,那1000元我没花,零花钱够用了。”
          妈妈:“恩,这才是明智的做法!”
          儿子:“妈妈,那1000元我没花,我要攒起来。”
          妈妈:“恩,加油!”

          • 是不是没完没了?只要儿子手中还有钱,他就会一直汇报,这就是LT模式。有钱就是1,没钱就是0,那么只要儿子还有钱,这种事件就是1->1类型事件,自然是LT。
        • ET 边缘触发

          儿子:“妈妈,我收到了5000元压岁钱。”
          妈妈:“恩,省着点花!”
          儿子:“……”
          妈妈:“你倒是说话啊?压岁钱呢?!”

          • 这个就是ET模式,简洁得有点过头,但很高效!虽然妈妈可能并不这么认为。。。儿子从没钱到有钱,是一个0->1的过程,因此为ET。儿子和妈妈说过自己拿到了压岁钱就完事了,至于怎么花钱,还剩多少钱,一概不说。
      • LT 模式和 ET 模式的区别是 ET 模式是高电平到低电平切换的时候或者低电平切换高电平才会触发。

        • EPOLLIN事件:

          • 内核的输入缓冲区 为空 低电平
          • 内核的输入缓冲区 不为空 高电平(一直触发EPOLLIN)
        • EPOLLOUT事件:

          • 内核发送缓冲区不满 高电平(一直触发EPOLLOUT)
          • 内核发送缓冲区满 低电平
      • 关于 LT 及 ET 的详细知识请点击此处

    • 什么时候关注EPOLLOUT事件呢?(如果在得到一个套接字马上关注,就会出现busy loop的状态)

      • LT 模式:在write的时候关注EPOLLOUT事件,如果数据没有写完,我们就需要把未发送完的数据添加到应用层缓冲区,然后关注这个连接套接字的EPOLLOUT事件,等到EPOLLOUT事件到来,取出应用层缓冲区的数据发送,如果应用层缓冲区数据发送完成,取消关注EPOLLOUT事件。

      • ET 模式:ET表示边缘触发,是电平从低到高或者从高到低才会触发。我们一开始就可以关注EPOLLIN事件和EPOLLOUT事件,不会出现busy loop。所以当接收缓冲区处于高电平状态时,一定要一次性把数据全部读完。因为如果一次没有读完,接收缓冲区仍然处于高电平状态,下次不会在触发EPOLLIN事件。同理,发送缓冲区的处理类似。

    注意

    • epoll 和 select/poll 本质上是一样的,性能上提升较大,都属于I/O多路复用模型(I/O Multiplexing Model),和阻塞,非阻塞两大I/O模型是并列的。只是对于事件处理函数角度而言,看起来是异步的(实际上是同步的回调而已)。和 epoll 组合使用大多数是非堵塞I/O方式,这时候就是同步非堵塞,也可以有堵塞I/O的情况。比如多线程server可以在 accept 上采用堵塞I/O,accept后的 socket 用 epoll 管理非堵塞的读写事件。

    • 使用 epoll 一定要加 定时器,否则后患无穷

    • 联合体 data 中的那个 ptr 是很有用的,只不过这就意味着你将该对象的生命周期交给了 epoll,不排除会有潜在bug的影响,需要辅以timeout

    • 多线程环境下使用epoll,多考虑 EPOLLONESHOT

    • EPOLLLT 也是一个不错的选择,除非你的框架能够确保每次事件触发后,都读/写至 EAGAIN

    • 使用前请仔细阅读 man 7 epoll

    总结

    • epoll 实现机制(简单介绍)

      • epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我们想监控的socket。这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。

      • 这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立 slab 层。简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象。

      • epoll的高效就在于,当我们调用 epoll_ ctl 往里塞入百万个句柄时,epoll_ wait 仍然可以飞快的返回,并有效的将发生事件的句柄给我们用户。

        • 这是由于我们在调用 epoll_ create 时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后 epoll_ ctl 传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件.
      • 当 epoll_ wait 调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait 非常高效。 而且,通常情况下即使我们要监控百万计的句柄,大多一次也只返回很少量的准备就绪句柄而已,所以,epoll_wait 仅需要从内核态copy少量的句柄到用户态而已。

      • 那么,这个准备就绪list链表是怎么维护的呢?

        • 当我们执行 epoll_ctl 时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。

        • 所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。

      • 执行 epoll_ create 时,创建了红黑树和就绪链表,执行 epoll_ ctl 时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行 epoll_wait 时立刻返回准备就绪链表里的数据即可。

    • 三种IO复用函数的分析

    参考

    展开全文
  • 主要介绍了linux内核select/poll,epoll实现与区别,需要的朋友可以参考下
  • epoll和select区别

    千次阅读 2016-10-13 10:17:08
    要了解epoll和select区别,首先来理解一下epoll和select各自的功能属性。poll和epoll和select都是多路复用下的一种机制,多路复用I/O就是通过一种机制,可以监视多个文件描述符,一旦某个文件描述符就绪,就通知...

    要了解epoll和select的区别,首先来理解一下epoll和select各自的功能和属性。poll和epoll和select都是多路复用下的一种机制,多路复用I/O就是通过一种机制,可以监视多个文件描述符,一旦某个文件描述符就绪,就通知程序该文件描述符可以进行读写操作。

    select():用于确定一个或多个套接口的状态。
    select的几大缺点(不可忽视):
    1、每次调用select()的时候,都必须要将fd从用户态转换成内核态,这个开销在fd很多的时候非常大。
    2、调用select()的时候,在操作系统内核API都会遍历整个fd集,这会大大影响系统效率。
    3、select()可打开的文件描述符的上限太少了,默认是1024个。

    epoll ():是Linux下为处理大批量文件描述符而作了改进的poll,是Linux下多路复用I/O接口poll/select的增强版本,能够显著的提高在大量并发连接中只有少数连接活跃的系统CPU利用率。它在获取事件的时候,不会将整个文件描述符集全部遍历,只需要遍历那些被系统API异步唤醒后放入Ready队列的文件描述符。有两种触发的方式,分别是边沿触发和水平触发方式。
    因为epoll是poll和select的改进版,所以对于select的缺点也作了相应的处理。epoll里面有三个函数,分别是:epoll_create(用于创建一个epoll句柄), epoll_wait(用于等待一个事件), epoll_ctl(用于注册要监听的事件类型).
    1、对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD)重点内容,会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
    2、对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。
    3、对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
    区别:1、select在每次被调用的时候,都会讲所有fd从用户态转换成内核态,而epoll只是在事件注册的时候只拷贝一次fd而已。提高了效率。
    2、对于select来说,在每次醒着的时候,都需要将整个fd遍历一遍,而对于epoll来说,只需要在current的时候挂一遍fd,然后设置一个回调函数,当设备准备完成时,就调用一个回调函数将对应的文件描述符返还给进程,所以在时间上要大大的提高于select。
    3、select的文件描述符的上限默认是1024,但是epoll没有这个限制,可以远大于1024,因为它只和系统的内存大小有关,而不受限于一个定值。

    展开全文
  • 问题1:关于 epoll select区别,哪些说法是正确的?(多选) A. epoll select 都是 I/O 多路复用的技术,都可以实现同时监听多个 I/O 事件的状态。 B. epoll 相比 select 效率更高,主要是基于其操作系统...

    问题1:关于 epoll 和 select 的区别,哪些说法是正确的?(多选)

    A. epoll 和 select 都是 I/O 多路复用的技术,都可以实现同时监听多个 I/O 事件的状态。
    
    B. epoll 相比 select 效率更高,主要是基于其操作系统支持的I/O事件通知机制,而 select 是基于轮询机制。 
    
    C. epoll 支持水平触发和边沿触发两种模式。
    
     D. select 能并行支持 I/O 比较小,且无法修改。
    

    出题人:阿里巴巴出题专家:寈峰/阿里技术专家
    参考答案:A,B,C 【延伸】那在高并发的访问下,epoll使用那一种触发方式要高效些?当使用边缘触发的时候要注意些什么东西?

    题目2:从 innodb 的索引结构分析,为什么索引的 key 长度不能太长?

    出题人:阿里巴巴出题专家:近秋/阿里云数据库产品技术部技术专家

    参考答案:key 太长会导致一个页当中能够存放的 key 的数目变少,间接导致索引树的页数目变多,索引层次增加,从而影响整体查询变更的效率。
    

    题目3: MySQL 的数据如何恢复到任意时间点?

    出题人:阿里巴巴出题专家:近秋/阿里云数据库产品技术部技术专家参考答案

    参考答案:恢复到任意时间点以定时的做全量备份,以及备份增量的 binlog 日志为前提。恢复到任意时间点首先将全量备份恢复之后,再此基础上回放增加的 binlog 直至指定的时间点。
    

    题目4: NFS 和 SMB 是最常见的两种 NAS(Network Attached Storage)协议,当把一个文件系统同时通过 NFS 和 SMB 协议共享给多个主机访问时,以下哪些说法是错误的:(多选)

    A. 不可能有这样的操作,即把一个文件系统同时通过 NFS 和 SMB协议共享给多个主机访问。
    
    B. 主机 a 的用户通过NFS 协议创建的文件或者目录,另一个主机 b的用户不能通过 SMB 协议将其删除。
    
    C. 在同一个目录下,主机 a 通过 NFS 协议看到文件 file.txt,主机b 通过 SMB 协议也看到文件 file.txt,那么它们是同一个文件。
    
    D. 主机 a 通过 NFS 协议,以及主机 b 通过 SMB 协议,都可以通过主机端的数据缓存,提升文件访问性能。
    

    出题人:阿里巴巴出题专家:起影/阿里云文件存储高级技术专家
    参考答案:A,B,C

    *

    题目5:输入 ping IP 后敲回车,发包前会发生什么?

    出题人:阿里巴巴出题专家:怀虎/阿里云云效平台负责人
    参考答案:

    首先根据目的IP和路由表决定走哪个网卡,再根据网卡的子网掩码地址判断目的IP是否在子网内。如果不在则会通过arp缓存查询IP的网卡地址,不存在的话会通过广播询问目的IP的mac地址,得到后就开始发包了,同时mac地址也会被arp缓存起来。
    

    题目6:请解释下为什么鹿晗发布恋情的时候,微博系统会崩溃,如何解决?

    出题人:阿里巴巴出题专家:江岚/阿里巴巴数据技术高级技术专家
    参考答案:

    A. 获取微博通过 pull 方式还是 push 方式
    B. 发布微博的频率要远小于阅读微博
    C. 流量明星的发微博,和普通博主要区分对待,比如在 sharding的时候,也要考虑这个因素
    
    展开全文
  • epoll和select区别 目前服务器主要采取接收请求的方式主要有三种,多线程接收请求, selectepoll。 Socket常见函数 1.创建serverSocket 2.bing (ip, port) 3. listen(serversocket的文件描述符,全链接队列长度...
  • 深入理解select、poll和epoll区别

    万次阅读 多人点赞 2019-05-17 19:56:49
    在此之前,我们先看一下epoll和select和poll的调用接口上的不同,select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数, epoll_create,epoll_ctl和epoll_wait,epoll_create 是创建一个...
  • linux提供了select、poll、epoll接口来实现IO复用,三者的原型如下所示,本文从参数、实现、性能等方面对三者进行对比。 代码如下: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, ...
  • Select和Epoll底层实现的区别

    千次阅读 2018-10-05 17:23:40
    Select,Poll和Epoll在OS底层实现上的区别,造成他们性能差距的根本原因
  • (1)select==>时间复杂度O(n) 它仅仅知道了,有I/O事件...poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,但是它没有最大连接数的限制,原因是它是基于链表来存.
  • 说一说select原理 select相关调用函数 void FD_ZERO(fd_set *set); // 将set清空为0 void FD_CLR(int fd, fd_set *set); // 将fd从set中清除出去 void FD_SET(int fd, fd_set *set); // 将fd设置到set中去 int FD_...
  • Select poll epoll 总结 摘要 先明确几个概念: 面试官问:给我讲讲什么事同步阻塞、异步阻塞、同步非阻塞、异步非阻塞。 我:????? 同步异步的概念  同步是指用户线程发起IO请求后,需要等待或者...
  • 详解select、poll和epoll之间的区别与优缺点
  • select的缺点: 支持的fd数量有限: 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;(在linux内核...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,668
精华内容 23,067
关键字:

epoll和select的区别