【Linux】I/O多路复用模型 select、poll、epoll

2024-06-21 03:44

本文主要是介绍【Linux】I/O多路复用模型 select、poll、epoll,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡>𖥦<)!!
主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步!
🔥Linux系列专栏:Linux基础 🔥

给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ

在这里插入图片描述


目录

    • 多路:
    • 复用:
  • 一、select 模型
  • 二、poll 模型
  • 三、epoll 模型
    • 水平触发模式 EPOLLLT
    • 边缘触发模式 EPOLLET
    • 示例

在监听 socket 时,需要分配多个线程/进程维护多个 socket 连接,I/O 多路复用技术就是用来使用一个进程来监听维护多个 socket 连接

多路:

I/O 状态:可读、可写

复用:

使用一个线程/进程监听处理 I/O 事件,复用多个 socket 请求


一、select 模型

select 原理:

  • 1.创建一个文件描述符集合 fd_set set 0 不监听 1 监听

  • 2.设置文件描述符的状态

    • FD_ZERO(&set); 初始化监听集合,将位码初始化为 0
    • FD_SET(int sockfd,&set); 将 sockfd 在集合中对应的位设置成 1
    • FD_CLR(int sockfd,&set); 将 sockfd 在集合中对应的位设置成 0
    • int code = FD_ISSET(int sockfd,&set); 返回 sockfd 在文件描述符集合中的位码
  • 3.调用 select 函数,传入文件描述符集合 set


select 函数:

#include <sys/select.h>
#include <unistd.h>int ready = select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* errorfds,struct timeval* timeout);

参数:

  • nfds: 需要监视的最大文件描述符值加 1
  • readfds:监听读事件,设为 NULL 不监听
  • writefds:监听写事件,设为 NULL 不监听
  • errorfds:异常事件
  • timeout:阻塞时间,设为 NULL 为阻塞监听,定义 timeval 结构体将成员设置为 0 为非阻塞。定时阻塞支持微妙级别定时

返回值:返回就绪的 socket 数量

select 函数会将整个文件描述符集合 set 拷贝到内核中,让内核检查是否有读/写事件,内核遍历文件描述符集合,
如果有事件发生,内核将对应的 socket 标记位可读/可写,然后将整个文件描述符集合 set 拷贝回用户空间,用户再遍历文件描述符集合找到就绪的 socket 进行后续处理


select 的缺点:

  • 1.Linux 平台受最大文件描述符数量限制,select 的最大监听数为 1024
  • 2.select 监听就绪后只返回就绪的数量,需要用户遍历找到就绪的 socket
  • 3.监听集合会在监听到就绪后被修改,需要用户自行分离传入传出设置
  • 4.轮询监听,轮询数量大, CPU 处理性能下降
  • 5.拷贝和挂载开销大
  • 6.设置监听不灵活,无法对不同的 socket 设置不同的监听类型

二、poll 模型

poll 监听事件种类更丰富,对监听和就绪数组进行了传入传出分离

poll 不受最大文件描述符数量限制,支持用户自定义长度结构体数组作为集合

#include <poll.h>int ready = poll(struct pollfd* fds, int nfds, int timeout);

参数:

  • fds:监听数组
    • struct pollfd listen_array[4096]; //监听数组
    • listen_array[0].fd = sockfd; //监听 sockfd,-1 取消监听
    • listen_array[0].events = POLLIN|POLLOUT|POLLERR; //设置监听事件
    • listen_array[0].revents; //如果监听的 socket 就绪,系统将就绪事件传到 revents 中
  • nfds:监听数组大小
  • timeout:阻塞时间,-1 为阻塞监听,0 为非阻塞。>0 定时阻塞 只支持毫妙级定时

poll 的缺点

  • 1.轮询监听,轮询数量大, CPU 处理性能下降
  • 2.拷贝和挂载开销大
  • 3.只有特定的 Linux 版本才能使用

三、epoll 模型

结合了 select 和 poll 的优势

创建监听树(创建于内核中,使用红黑树):

#include <sys/epoll.h>
int epfd = epoll_create(int size);

参数:size 为监听数量。返回值:指向监听树的描述符。


监听树节点

struct epolevent node;
node.data.fd = sockfd;
node.events = EPOLLIN|EPOLLOUT|EPOLLERR;

epoll 监听到就绪直接返回就绪节点,用户遍历处理这些就绪 socket 即可


向监听树中添加节点:

epoll_ctl(int epfd,EPOLL_CTL_ADD,int sockfd,struct epollevent* node);

在监听树中删除节点:

epoll_ctl(int epfd,EPOLL_CTL_DEL,int sockfd,NULL);

修改监听树中的节点(修改监听的事件):

epoll_ctl(int epfd,EPOLL_CTL_MOD,int sockfd,&node);

监听事件发生:

int ready = epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

参数:

  • epfd:监听树
  • events:监听事件数组
  • maxevents:最大就绪数大小
  • timeout:阻塞时间,-1 为阻塞监听,0 为非阻塞。>0 定时阻塞

epoll 的优点:

  • 没有监听数量限制,不用担心轮询问题
  • epoll 使用了事件驱动的方式,监听集合在内核层,避免了每次调用时的重复设置和遍历操作。内核会将就绪的文件描述符直接返回
  • 监听事件种类更丰富,可以对不同的 socket 设置不同的事件监听,对监听和就绪数组进行了传入传出分离


水平触发模式 EPOLLLT

  • 当可读或可写事件发生时,epoll 会持续通知用户处理直到用户处理。
  • 以在任何时候处理这些通知,不必立即响应
  • 水平触发适用于处理数据量较大或需要缓冲数据的情况,因为可以确保用户不会错过任何数据

多个事件同时发生可能导致重复处理,可以使用 EPOLLONESHOT设置为只触发一次通知


边缘触发模式 EPOLLET

  • 当可读或可写事件发生时,epoll 会立即通知(只会通知 1 次)
  • 应用程序需要在收到通知后立即处理相应的 I/O 事件,否则可能会丢失事件
  • 边缘触发适用于处理大量并发连接且数据量较小的情况,可以减少不必要的系统调用和上下文切换。

边缘触发模式一般和非阻塞 I/O 搭配使用,直到系统调用(如 read 和 write)返回错误,错误类型为 EAGAIN 或 EWOULDBLOCK


示例

    //创建监听树并将serversock设置监听读事件if((epfd = epoll_create(EPOLL_MAX)) == -1){perror("epoll_initializer: create epoll tree failed");}struct epoll_event node;node.data.fd = sockfd;node.events = EPOLLIN;//添加节点epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&node);int readycode;struct epoll_event readyarray[EPOLL_MAX];int i = 0;int flag = 1;while(flag){if((readycode = epoll_wait(epfd,readyarray,EPOLL_MAX,-1)) == -1){perror("epoll_wait failed");}else{i = 0;//根据readycode就绪数量处理就绪while(readycode){//判断就绪if (readyarray[i].data.fd == server_sockfd){//server sock ready//添加tcp连接任务}else{//client sock ready//添加响应处理任务}--(readycode);++i;}}}

在这里插入图片描述

大家的点赞、收藏、关注将是我更新的最大动力! 欢迎留言或私信建议或问题。
大家的支持和反馈对我来说意义重大,我会继续不断努力提供有价值的内容!如果本文哪里有错误的地方还请大家多多指出(●'◡'●)

这篇关于【Linux】I/O多路复用模型 select、poll、epoll的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080118

相关文章

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu