【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命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将