select 套select_epoll什么时候比select慢?

    我们都知道nginx、redis使用了epoll技术,为什么epoll能这么快呢?

    我们先来熟悉熟悉IO的基础概念。

1.Unix IO模型

    对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
    1.等待数据准备
    2.将数据从内核拷贝到应用进程中

    Unix下有5种IO模型:

1.1 同步IO

        同步IO包含以下四种IO:

    • 阻塞式IO

          发起IO请求后,应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。对应java中的实现就是我们常说的BIO。

    • 非阻塞式IO

          发起IO请求后,应用进程可以继续执行,但需要不断的执行系统调用来获知IO是否完成。

    • IO复用(select和poll)

          发起IO请求后,应用进程可以继续执行,不需要不断的执行系统调用来或者IO是否完成。对应在java中的实现即为NIO。

          使用 select 或者 poll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中。它可以让单个进程具有处理多个IO事件的能力。又被称为Event Driven IO,即事件驱动 IO。

    • 信号驱动式IO

          发起IO请求后,应用进程可以继续执行,应用进程使用sigaction调用,内核在数据到达时向应用进程发送SIGIO信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。

1.2 异步IO

    应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。对应java实现也就是jdk1.7之后的AIO。

1.3 五种IO模型对比

    同步IO:将数据从内核缓存区复制到用户进程时(第二阶段)会发生阻塞。

   异步IO:整个过程都不会发生阻塞。

   同步IO中,阻塞式IO会在第一阶段(发起IO请求后)阻塞。非阻塞IO、IO复用和信号驱动IO在第一阶段都不会发生阻塞,只是在第二阶段时发生阻塞。

75e28752c753773805d2fe9c0a606c81.png

2. Unix的IO多路复用

    select/poll/epoll 都是IO多路复用的具体实现。

    IO多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。

    select,poll,epoll本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

2.1 select 

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有异常),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset(时间复杂度O(n)),来找到就绪的描述符。

    select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024。

2.2 poll 

int poll(struct pollfd *fds, unsigned int nfds, int timeout);

    poll 的功能与 select 类似,也是等待一组描述符中的一个成为就绪状态。poll 中的描述符是 pollfd 类型的数组,pollfd 的定义如下:

eebd11bcea213108c706eb4083ba3f20.png

    pollfd并没有最大数量限制(但是数量过大后性能也是会下降),和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符(时间复杂度O(N))。

    1.select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

    2.select 和 poll 速度都比较慢,每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。

2.2 epoll 

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

2.2.1 epoll_create;

    创建一个epoll的句柄,当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

    epoll的结构体:

85c51bab4d4ce59ee4d934b13b1462f4.png

     eventpoll事件都会挂载在红黑树中的,读取时间复杂度是O(logn)。

2.2.2 epoll_ctl;

对指定描述符fd执行op操作:

    添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。

epoll_event:告诉内核需要监听什么事件

    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读;
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

2.2.3 epoll_wait
    等待epfd上的io事件。

2.2.4 epoll优势

    select/poll每次调用时都要传递你所要监控的所有socket给系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。

    而调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。

    select监控的句柄列表在用户态,每次调用都需要从用户态将句柄列表拷贝到内核态,但是epoll中句柄就是建立在内核中的,这样就减少了内核和用户态的拷贝,高效的原因之一。

2.3 select/poll/epoll 应用场景

    1.select 的 timeout 参数精度为微秒,而 poll 和 epoll 为毫秒,因此 select 更加适用于实时性要求比较高的场景。select 可移植性更好,几乎被所有主流平台所支持。

    2.poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。

    3.epoll需要运行在 Linux 平台上,有大量的描述符需要同时轮询,并且这些连接最好是长连接。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。如果没有大量的idle-connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle-connection,就会发现epoll的效率大大高于select/poll。

再回到最初的问题:epoll什么时候比select慢?

ans: 在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值