当前位置: 首页 > article >正文

【Linux杂货铺】IO多路复用

目录

🌈前言🌈

📁 五种IO模型

 📂 阻塞IO

 📂 非阻塞IO

 📂 信号驱动IO

 📂 多路复用

 📂 异步IO

📁 非阻塞IO实现

📁 select

 📂 接口使用

 📂 缺点

📁 poll

 📂 接口使用

 📂 优缺点

📁 epoll

 📂 接口使用

 📂 底层原理

 📂 工作模式 ET vs LT

 📂 优点

📁 总结


🌈前言🌈

        本期【Linux杂货铺】,主要讲解什么是IO多路复用,通过了解IO模式,了解为什么要有多路复用,以及其他的IO模型,学习多路复用的3中模型。

        

📁 五种IO模型

 📂 阻塞IO

        在内核将数据准备好之前,系统调用会一直等待。默认是阻塞方式。

 📂 非阻塞IO

        如果内核还没有将数据准备好,系统调用会直接返回,并且返回EWOULDBLOCK错误码。此时,就需要特殊判错误码,究竟是异常错误,还是非阻塞IO。

        非阻塞IO常常需要循环式的读写这个文件描述符,这个过程叫做轮询,对CPU是一个巨大的浪费,只有在特定场景下才会使用。

 📂 信号驱动IO

        内核将数据准备好,使用SIGIO信号通知应用程序进行IO操作。

 📂 多路复用

        同时等待多个文件描述符的就绪状态,如果有一个或多个文件描述符事件触发,就会返回。

 📂 异步IO

        由内核在数据拷贝完成时,通知应用程序,上层只需要完成数据处理即可。但是异步IO往往伴随着代码逻辑复杂,难以理解,编程困难等问题。

        在IO过程中,需要经过两个步骤,1. 等待;2. 拷贝。等待的时间往往高于拷贝时间,因此提高IO效率,最核心的方法就是缩小等待时间。

        此外,同步和异步关注的消息通知机制

        同步,就是发出一个调用时,在没有得到结果之前,该调用就不返回。一旦调用返回,就得到返回值,即由调用者主动等待这个调用结果;

        异步,调用发出之后,这个调用会直接返回,没有返回结果,即一个异步调用发出后,调用者不会立刻得到结果,而是调用发出后,被调用者通过,信号等通知调用者,通过回调函数处理这个调用。

        阻塞和非阻塞关注的是程序等待调用结果时状态。

        阻塞调用指的是调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果才会返回。

        非阻塞调用指的是不能立刻得到结果之前,该调用不会阻塞当前线程。

📁 非阻塞IO实现

        文件描述符默认都是阻塞的。使用fcntl系统调用可以修改文件描述为非阻塞。

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd ,int cmd , ...);

fcntl函数有5种功能:

        1. 复制现有的描述符(cmd = F_DUPFD)

        2. 获得/设置文件描述符标记(cmd = F_GETFD 或 F_SETFD)

        3. 获得/设置文件状态标记(cmd = F_GETFL 或 F_SETFL)

        4. 获得/设置异步IO所有权(cmd = F_GETOWN或F_SETOWN)

        5. 获得/设置锁(cmd = F_GETLK,F_SETLK或F_SETLKW)

void SetNoBlock(int fd)
{
    int fl = fcntl(fd , F_GETFL);
    if(fl < 0)
    {
        prror("fcntl");
        return ;
    }
    fcntl(fd , F_SETFL , fl | O_NONBLOCK);
}

📁 select

 📂 接口使用

include <sys/select.h>

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

        • 参数 nfds 是需要监视的最大的文件描述符值+1;

        • rdset,wrset,exset 分别对应于需要检测的可读文件描述符的集合, 可写文件描述符的集合及异常文件描述符的集合;

        • 参数 timeout 为结构体 timeval, 用来设置 select()的等待时间。timeout为NULL时,表示select会一直被阻塞,直到事件发生;timeout为0时,仅检测描述符集合的状态,然后立刻返回,并不会等待外部事件的发生;timeout为特定时间值,如果指定时间段内没有事件发生,select会超时返回。

        fd_set结构表示为1个位图,一个bit位表示要监视文件描述符。select系统调用的三个fd_set参数是输入输出型参数,输入表示要监听哪些描述符,输出表示哪些描述符事件就绪。


void FD_CLR(int fd, fd_set *set); // 用来清除描述词组 set 中相关fd的位

int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组 set 中相关fd的位是否为真

void FD_SET(int fd, fd_set *set); // 用来设置描述词组 set 中相关fd的位

void FD_ZERO(fd_set *set); // 用来清除描述词组 set 的全部位

        select系统调用返回值:执行成功返回文件描述符状态已改变的个数;如果返回值为0表示超时;如果返回-1,错误原因存在于errno。

        select系统调用常常搭配一个数组来使用。将需要监听的描述符添加至数组,通过遍历数组来给fd_set赋值。

 📂 缺点

        1. select函数,支持的文件描述符数量太小;

        2. select函数,每次都需要手动设置fd集合,使用不方便;

        3. 每次调用select都需要在内核遍历传递进来所有的fd,这个开销在fd很多时也很大。

        4. 每次调用select,都需要吧fd集合从用户态拷贝到内核态,这个开销在fd很多时很大。
 

📁 poll

 📂 接口使用


include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd 结构
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};

        • fds 是一个 poll 函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.

        • nfds 表示 fds 数组的长度.

        • timeout 表示 poll 函数的超时时间, 单位是毫秒(ms)

        如果底层事件就绪,revents会被赋值。

 events和revents的取值:

        poll函数返回值小于0,表示出错;返回值等于0,表示poll等待超时;返回值小于0,表示由于poll由于文件描述符就绪而返回。 

 📂 优缺点

        1. poll没有描述符的数量限制;接口比select使用更方便,pollfd结构体包含了要监视的event和发生的revent,不在使用select输入输出型参数传递方式。

        2. 同select一样,poll返回后,需要遍历pollfd数组来获取就绪的描述符。

        3. poll需要把大量的pollfd结构体从用户态拷贝至内核态。

📁 epoll

 📂 接口使用

1. 创建epoll文件描述符(句柄)
int epoll_create(int size);

2.操作epoll模型
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

3. 等待时间
int epoll_wait(int epfd , struct epoll_event* events,int maxevents , int timeout);

op参数表示动作,用3个宏来表示:

        • EPOLL_CTL_ADD: 注册新的 fd 到 epfd 中;

        • EPOLL_CTL_MOD: 修改已经注册的 fd 的监听事件;

        • EPOLL_CTL_DEL: 从 epfd 中删除一个 fd;

uint32_t events可以是一下几个宏的集合:

        • EPOLLIN : 表示对应的文件描述符可以读 (包括对端 SOCKET 正常关闭);

        • EPOLLOUT : 表示对应的文件描述符可以写;

        • EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来);

        • EPOLLERR : 表示对应的文件描述符发生错误;

        • EPOLLHUP : 表示对应的文件描述符被挂断;

        • EPOLLET : 将 EPOLL 设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的.

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

        epoll_wait系统调用会将底层已经就绪的事件拷贝至数组中,系统调用返回值就是已经就绪的事件数量n,只需要遍历数组前n个元素即可。

 📂 底层原理

        epoll模型中,节点采用了与类型无关的数据结构,即一个节点可以被连入到不同的数据结构中,既可以在红黑树中,也可以在就绪队列中。

        当节点既在红黑树中中,又在就绪队列中,就表示该节点监视的文件描述中有事件触发。

 📂 工作模式 ET vs LT

        水平触发(LT):epoll默认状态下就是LT模式。当epoll检测到事件就绪,如果没有处理,或者处理不完全,再次调用epoll_wait时会立即返回并通知事件就绪,直到事件被处理完成,如缓冲区上所有数据被处理完成,epoll_wait才不会立刻返回。支持阻塞读写或非阻塞读写。

        边缘触发(ET):epoll检测到有事件就绪,必须立刻处理。如果没有处理,或者处理不完全,下次调用epoll_wait不会再通知,而是等到有新的数据到来,事件再次就绪,再次通知上层。即事件就绪后,只有一次处理机会。ET模式比LT模式性能更高,Nginx默认采用ET模式。ET模式只支持非阻塞读写。

        ET模式的epoll,需要将文件描述符设置为非阻塞,这不是接口上的要求,而是“工程实践”上的要求。

        如果面对下面这种情况,ET模式采用阻塞读取是会出现问题的:

• 服务器只读到 1k 个数据, 要 10k 读完才会给客户端返回响应数据.

• 客户端要读到服务器的响应, 才会发送下一个请求

• 客户端发送了下一个请求, epoll_wait 才会返回, 才能去读缓冲区中剩余的数据

        因此,为了解决上述问题,需要将ET模式改为非阻塞轮询式读取底层数据,一次将数据全部读取完毕。

 📂 优点

        1. 接口使用方便。

        2. 数据拷贝轻量。

        3. 事件回调机制,避免了使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入就绪队列中。

        4. 没有数量限制。

📁 总结

        以上,就是本期【Linux杂货铺】的主要内容了,主要讲解了五种IO模型,重点讲解了select,epoll接口,学习了epoll底层原理,以及epoll的工作模式,了解了poll接口,其中epoll是尤为重要的多路复用接口。

        如果感觉本期内容对你有帮助,欢迎点赞,关注,收藏Thanks♪(・ω・)ノ


http://www.kler.cn/a/388615.html

相关文章:

  • 使用 Visual Studio Installer 彻底卸载 Visual Studio方法与下载
  • 设计模式:工厂方法模式和策略模式
  • 软件测试面试题(800道)【附带答案】持续更新...
  • Ue5 umg学习(一)
  • Chromium 中MemoryMappedFile使用例子c++
  • Tomcat与Nginx之全面比较
  • C# const与readonly关键字的区别
  • 通过API接口探索电商平台商品详情:一站式接入指南
  • 【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
  • 嵌入式课程day10-C语言数组
  • 使用react+copy-to-clipboard封装双击复制组件
  • vue3 传值的几种方式
  • 机器学习(五)——支持向量机SVM(支持向量、间隔、正则化参数C、误差容忍度ε、核函数、软间隔、SVR、回归分类源码)
  • FPGA 第5讲 点亮你的LED灯
  • Windows下AMD显卡配置pyTorch记录
  • 云计算基础知识
  • Spring Boot架构下的工程认证计算机课程管理
  • 7.4、实验四:RIPv2 认证和触发式更新
  • 适用于 Windows 11/10 电脑 的 13 个最佳文件恢复软件
  • window下安装rust 及 vscode配置
  • 《一本书讲透 Elasticsearch》京东评论采集+存储+可视化全 AI 实现
  • 《数据结构》--二叉树【上】
  • ubuntu下安装 git 及部署cosyvoice(2)
  • 【开源社区】ELK 磁盘异常占用解决及优化实践
  • 如何平滑切换Containerd数据目录
  • android 适应CA证书