• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2021-12-12 13:07 Aet 隐藏边栏 |   抢沙发  4 
文章评分 1 次,平均分 5.0

三组I/O复用对比

概述

  1. 上述select,poll和epoll都能同时监听多个文件描述符。它们将等待由timeout参数指定的超时时间,直到一个或多个文件描述符上有事件发生时,返回值是就绪的文件描述符的数量。返回0表示没有事件发生。
  2. 这3个函数都通过某种结构体变量来告诉内核监听哪些文件描述符上的哪些事件,并使用该结构体类型的参数来获取内核处理的结果。

select

  1. select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符集合,因此select还需要提供3个这种类型的参数来分别传入和输出可读、可写或异常等事件。
  2. 这一方面使得select不能处理更多类型的事件,另一方面由于内核对fd_set集合的在线修改,应用程序下次调用select前不得不重置这3个fd_set集合。

poll

  1. poll的参数类型pollfd更“聪明”一些。因为它把文件描述符和事件都定义在结构体当中了,任何事件都被统一处理,从而使得编程接口简洁得多。
  2. 并且内核每次修改的都是pollfd结构体的revents成员,而events成员保持不变,因此下次调用poll时应用程序无须重置pollfd类型的事件集参数。
  3. 由于每次select和poll调用都返回整个用户注册的事件集合(其中包括就绪的未就绪的),所以应用程序索引就绪文件描述符的时间复杂度为O(n)。

epoll

  1. epoll则采用了与select和poll完全不同的方式来管理用户注册的事件。它在内核中维护一个事件表,并提供了一个独立的系统调用epoll_ctl来控制往其中添加、删除、修改事件。
  2. 这样,每次epoll_wait调用都直接从该内核事件表中取得用户注册的事件,而无须反复从用户空间读入这些事件。
  3. epoll_wait系统调用的events参数仅用来返回就绪的事件,这使得应用程序索引就绪文件描述符的时间复杂度达到了O(1)。

综合

  1. poll和epoll_wait分别用nfds和maxevents参数指定最多监听多少个文件描述符和事件。
    这两个数值都能达到系统允许打开的最大文件描述符数目,即65535个(cat/proc/sys/filemax)。
    而select允许监听的最大文件描述符数量通常有限制,虽然用户可以修改这个限制,但可能会导致不可预期的后果。
  2. select和poll都只能工作在效率相对较低的LT模式,而epoll可以工作在效率较高的ET模式。
    并且epoll还支持EPOLLONESHOT事件,该事件能进一步减少可读、可写和异常等事件被触发的次数。
  3. 从实现原理上来讲:
    1. select和epoll都是轮询的方式,即每次调用都要扫描整个注册文件描述符集合,并且将其中就绪的文件描述符返回给用户程序,因此它们检测就绪事件的算法的时间复杂度是O(n)。
    2. epoll_wait则不同,它采用的是回调的方式。内核检测到就绪的文件描述符时,将触发回调函数,回调函数就将该文件描述符上对应的事件插入内核就绪事件队列。
      内核最后在适当的时机将该就绪事件队列中的内容拷贝到用户空间。
      因此epoll_wait无须轮询整个文件描述符集合来检测哪些事件已就绪,其算法时间复杂度是O(1)。
  4. 需要注意的是,当活动连接比较多时,epoll_wait的效率未必比select和poll高,因为此时回调函数被触发得过于频繁。
    所以epoll_wait适用于连接数量多,但活动连接较少的情况。

I/O复用应用:非阻塞connect

  1. connect系统调用的手册中描述了connect出错时的一种errno值:EINPROGRESS。
    这种错误发生在对非阻塞的socket调用connect,而连接又没有立即建立时。
    根据文件解释,这种情况下,可以调用select、poll等函数来监听这个连接失败的socket上的可写事件。
    当select、poll等函数返回后,再利用getsockopt来读取错误码并清除该socket上的错误。
    如果错误码是0,表示连接成功建立,否则连接失败。
  2. 下面的代码存在移植性问题:
    1. 非阻塞的socket可能导致connect始终失败
    2. select对于处于EINPROGRESS状态下的socket可能不起作用
    3. 对于出错的socket,getsockopt在有些系统(比如Linux)上返回-1,而在有些系统(比如原子伯克利的UNIX)上则返回0。

I/O复用应用:聊天室

客户端

服务器

I/O复用应用:同时处理TCP和UDP服务

超级服务xinet

  1. Linux因特网inetd是超级服务。它能同时管理着多个子服务,即监听多个端口。
  2. 现在Linux系统上使用的inetd服务程序是其升级版本xinetd。
    xinetd原理与inetd相同,但增加了一些控制选项,并提高了安全性。

配置文件

  1. xinetd采用/etc/xinetd.conf主配置文件和/etc/xinetd.d目录下的子配置文件来管理所有服务。
  2. 主配置文件包含的是通用选项,这些选项将被所有子配置文件继承。不过子配置文件可以覆盖这些选项。
    每一个子配置文件用于设置一个子服务的参数。
  3. /etc/xinetd.d/telnet如下图:

xinetd工作流程

  1. xinetd管理的子服务中有的是标准服务,比如时间日期服务daytime,回射服务echo和丢弃服务discard
  2. xinetd服务器在内部直接处理这些服务。还有的字符为则需要调用外部的服务器程序来处理。
    xinetd通过调用fork和exec函数来加载运行这些服务器程序。比如telnet,ftp

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享