• 【Linux网络编程】 I/O复用


    总结《Linux高性能服务器编程》第9章

    第9章 I/O复用

    • I/O复用使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要:
      • 客户端程序要同时处理多个socket,或同时处理用户输入和网络连接;
      • 服务器要同时监听多个端口,或者处理多种服务,或者同时处理TCP请求和UDP请求;
      • TCP服务器要同时处理监听socket和连接socket;
    • I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的
    • Linux下实现I/O复用的系统调用主要有select、poll和epoll

    select系统调用

    • 用途:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件;

    • select API
      #include<sys/select.h>
      int select(int nfds, fd_set* readfds, fd_set*writefds, fd_set* exceptfds, struct timeval* timeout);
      
      • 1
      • 2
      • nfds参数指定被监听的文件描述符的总数;
      • readfds、writefds和exceptfds参数分别指向可读、可写和异常事件对应的文件描述符集合;
      • timeout参数用来设置select函数的超时时间;
      • 返回值:
        • 成功时返回就绪(可读、可写和异常)文件描述符的总数;
        • 超时时间内没有任何文件描述符就绪,select将返回0;
        • 如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR;
        • 失败时返回-1并设置errno;
    • 文件描述符就绪条件
      • 在网络编程中,下列情况下socket可读
        • socket内核接收缓存区中的字节数大于或等于其低水位标记SO_RCVLOWAT;
        • socket通信的对方关闭连接,此时对该socket的读操作将返回0;
        • 监听socket上有新的连接请求;
        • socket上有未处理的错误;
      • 下列情况下socket可写
        • socket内核发送缓存区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT;
        • socket的写操作被关闭;
        • socket使用非阻塞connect连接成功或者失败(超时)之后;
        • socket上有未处理的错误;
      • select能处理的异常情况:接收到带外数据;
    • 处理带外数据
      • socket上接收到普通数据和带外数据都将使select返回,但socket处于不同的就绪状态:
        • 普通数据使socket处于可读状态;
        • 带外数据使socket处于异常状态;

    poll系统调用

    • 在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者;

      #include<poll.h>
      int poll(struct pollfd* fds, nfds_t nfds, int timeout);
      
      • 1
      • 2
      • fds参数是一个pollfd结构类型的数组,指定所有用户感兴趣的文件描述符上发生的可读、可写和异常等事件;

        struct pollfd
        {
        int fd;/*文件描述符*/
        short events;/*注册的事件,fd上一系列事件的按位或*/
        short revents;/*实际发生的事件,由内核填充*/
        };
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvABfJDr-1668427830146)(img/Linux高性能服务器编程/image-20221027082333481.png)]

      • nfds参数指定被监听事件集合fds的大小;

      • timeout参数指定poll的超时值;

      • poll系统调用的返回值的含义与select相同;

    epoll系列系统调用

    • epoll是Linux特有的I/O复用函数,在实现和使用上与select、poll有很大差异:

      • epoll使用一组函数来完成任务,而不是单个函数;
      • epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,无须重复传入事件集;
      • epoll需要使用一个额外的文件描述符来标识内核事件表;
    • 内核事件表
      • 创建文件描述符标识内核事件表

        #include<sys/epoll.h>
        int epoll_create(int size)
        
        • 1
        • 2
      • 操作内核事件表

        #include<sys/epoll.h>
        int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event)
        
        • 1
        • 2

        fd参数是要操作的文件描述符,op参数指定操作类型,event参数指定事件;

        • EPOLL_CTL_ADD,往事件表中注册fd上的事件;
          EPOLL_CTL_MOD,修改fd上的注册事件;
          EPOLL_CTL_DEL,删除fd上的注册事件;
    • epoll_wait函数
      • epoll系列系统调用的主要接口是epoll_wait函数。它在一段超时时间内等待一组文件描述符上的事件

        #include<sys/epoll.h>
        int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
        
        • 1
        • 2
        • epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中复制到第二个参数events指向的数组中;
        • events数组只用于输出epoll_wait检测到的就绪事件,而不像select和poll的数组参数那样既用于传入用户注册的事件,又用于输出内核检测到的就绪事件,从而提高应用程序索引就绪文件描述符的效率
    • LT和ET模式
      • epoll对文件描述符的操作有两种模式:LT(Level Trigger,电平触发)模式和ET(Edge Trigger,边沿触发)模式;
      • 采用LT工作模式的文件描述符:当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件,当应用程序下一次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件;
      • 采用ET工作模式:应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件;
      • ET模式在很大程度上降低了同一个epoll事件被重复触发的次数,因此效率要比LT模式高;
    • EPOLLONESHOT事件
      • 实现同一时刻只有一个线程在为某个socket服务,但一个socket在不同时间可能被不同的线程处理;

    三组I/O复用函数的比较

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ATim5JnO-1668427830147)(img/Linux高性能服务器编程/image-20221027092135070.png)]

    I/O复用的高级应用

    • 非阻塞connect
      • 对非阻塞的socket调用connect,而连接又没有立即建立时,可以调用select、poll
        等函数来监听这个连接失败的socket上的可写事件,函数返回后再利用getsockopt来读取错误码并清除该socket上的错误,这种非阻塞connect方式能同时发起多个连接并一起等待

    代码清单9-5 非阻塞connect

    • 聊天室程序
      • 使用I/O复用同时处理网络连接和用户输入;
      • 客户端:从标准输入终端读入用户数据,并将用户数据发送至服务器;往标准输出终端打印服务器发送给它的数据;
        • 使用poll同时监听用户输入和网络连接,利用splice函数将用户输入内容直接定向到网络连接上发送,实现数据零拷贝,提高了程序执行效率;
      • 服务器:接收客户数据,并把客户数据发送给每一个登录到该服务器上的客户端;
        • 使用poll同时管理监听socket和连接socket;

    代码清单9-7 聊天室服务器程序

    • 回射服务器
      • 在实际应用中,有不少服务器程序能同时监听多个端口,比如超级服务inetd和android的调试服务adbd
      • 服务器如果要同时监听多个端口,就必须创建多个socket,并将它们分别绑定到各个端口上;
      • 如果服务器要同时处理该端口上的TCP和UDP请求,则也需要创建两个不同的socket:流socket和数据报socket,并将它们都绑定到该端口上;

    代码清单9-8 同时处理TCP请求和UDP请求的回射服务器

    超级服务xinetd

    • Linux因特网服务inetd是超级服务,它同时管理着多个子服务,即监听多个端口;
    • xinetd是inetd的升级版,原理与inetd相同,但增加了一些控制选项,并提高了安全性。
  • 相关阅读:
    Linux中7种文件类型
    非零基础自学Java (老师:韩顺平) 第6章 数组、排序和查找 6.12 多维数组 - 二维数组 && 6.13 二维数组的使用
    Nuxt - 页面缓存(从列表进入详情页返回后保持原来位置的搜索条件、结果数据、浏览器滚动条位置等)
    【DETR】
    施努卡:什么是视觉定位系统 视觉系统如何定位
    最大似然估计的理解
    yolov5中pt模型转onnx模型报错
    深度学习_1 介绍;安装环境
    初级算法_数组 --- 只出现一次的数字
    维基百科是如何定义联合办公空间的?
  • 原文地址:https://blog.csdn.net/weixin_42659457/article/details/127854508