10.select poll epoll

#include <sys/select.h>   
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

在 Linux 中, 我们可以使用 select 函数实现 I/O 复用, 给 select 函数传递的参数会告诉内核:

  • 我们所关心的文件描述符
  • 对每个描述符, 我们所关心的状态(读 写 异常).
  • 我们要等待多长时间(我们可以等待无限长的时间, 等待固定的一段时间, 或者根本就不等待).

select 函数返回后, 内核告诉我们一下信息:

  • 对我们的要求已经做好准备的描述符的个数.
  • 对于三种条件哪些描述符已经做好准备.

虽然 select 函数, 可以监听多个客户端链接, 有效减少了线程数量.

但是缺点也比较明显:

  1. 文件描述符数量并不是无限的(1024).
  2. 当文件描述符过多 IO 效率就会降低.
  3. 当每次调用 select 函数时, 都会将文件描述符集合从用户态拷贝到内核态.

在这里插入图片描述
for 循环 n 次 中的 n 就表示我们所关心的文件描述符, 每判断一次描述符就要循环 32 次, 但是要注意, 如果循环的描述符 大于等于最大描述符+1 时, 循环也会结束.

循环 32 次是因为要判断当前位置有没有注册, 如果没有就会进入下一次循环, 否则就判断数据哪种事件(读 写 异常).

下面是一部分主要代码:

int do_select(int n, fd_set_bits *fds, struct timespec *end_time) {
    int retval, i, timed_out = 0;
    retval = max_select_fd(n, fds); //获取最大文件描述符

    if (retval < 0)
        return retval;
    n = retval;

    retval = 0;
    for (;;) {
        for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
            for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
                int fput_needed;
                if (i >= n)
                    break;
                if (!(bit & all_bits))
                    continue;
            }
        }
    }

    return retval;
}

poll

poll 本质上和 select 没有区别, 它将用户传入的数组拷贝到内核空间, 然后查询每个 fd 对应的设备状态, 如果设备就绪则在设备等待队列中加入一项并继续遍历, 如果遍历完所有 fd 后没有发现就绪设备, 则挂起当前进程, 直到设备就绪或者主动超时, 被唤醒后它又要再次遍历 fd. 这个过程经历了多次无谓的遍历.

虽然 poll 没有最大描述符限制, 但是有一个 “水平触发” 特点, 也就是说当发现就绪的 fd 后, 没有被处理, 那么下次 poll 还会报告这个 fd.

epoll

epoll 是基于事件驱动的 I/O 方式, 相对于 selectpoll 来说, epoll 没有描述符个数限制, 从用户态拷贝到内核态只需要一次. 优点:

没有最大并发连接的限制, 1G 的内存能监听约 10 万个端口.

不是使用轮询方式, 而是使用回调方式. 会使用 callback 函数通知, 而且内核和用户控件使用同一块内存.

三种方式对比

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值