Linux 网络编程:select 与 epoll IO 多路复用详解

IO模型 + select、epoll 核心知识点

标签:Linux网络编程 IO多路复用 select epoll TCP并发

目录

  • 一、4种常见IO模型
  • [二、select 知识点](#二、select 知识点)
    • [1. fd_set 集合操作宏](#1. fd_set 集合操作宏)
    • [2. select函数](#2. select函数)
    • [3. select缺点(考试重点)](#3. select缺点(考试重点))
  • [三、epoll 三大API](#三、epoll 三大API)
    • [1. epoll_create](#1. epoll_create)
    • [2. epoll_ctl](#2. epoll_ctl)
    • [3. epoll_wait](#3. epoll_wait)
  • [四、epoll while(1)循环核心逻辑(高频代码考点)](#四、epoll while(1)循环核心逻辑(高频代码考点))
  • [五、select VS epoll对比](#五、select VS epoll对比)
  • 六、事件标记

一、4种常见IO模型

  1. 阻塞IO

    没有数据时CPU将任务挂起,任务不占用CPU资源,效率高。

  2. 非阻塞IO

    没有数据时,程序不停轮询检测数据是否到来,CPU持续执行代码,效率低。

  3. 信号驱动IO(异步IO)

    把fd设置为异步IO,内核监测fd事件;事件发生内核发送SIGIO信号通知用户层,程序捕获信号处理IO。

  4. 多路复用IO

    由内核同时监听一堆文件描述符,只要其中任意fd就绪,就返回给应用层,主要实现:selectepoll

补充:多线程TCP并发:

每个客户端分配一个独立线程;线程拥有独立栈空间,并发数量有限,客户端太多有系统崩溃风险;实际开发一般搭配线程池使用。

二、select 知识点

1. fd_set 集合操作宏

作用
FD_ZERO(fd_set *set) 清空文件描述符集合
FD_SET(int fd, fd_set *set) 将fd加入集合
FD_CLR(int fd, fd_set *set) 把fd从集合清除
FD_ISSET(int fd, fd_set *set) 判断fd是否在就绪集合中

2. select函数

c 复制代码
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • 参数
    1. nfds最大文件描述符 + 1
    2. readfds:读事件fd集合;writefds:写事件fd集合;exceptfds:异常事件集合
    3. timeout:超时时间。传NULL代表永久阻塞等待事件。
  • 返回值
    *

    0:就绪的fd个数

    • -1:函数出错
    • 0:超时,没有任何事件发生

3.select缺点(考试重点)

  1. 返回后集合会被内核修改,每次循环必须重新FD_ZERO、FD_SET
  2. 需要循环从0~nfds‑1遍历全部fd,调用FD_ISSET判断是否就绪;
  3. 文件描述符数量有上限。

三、epoll 三大API

1. epoll_create

c 复制代码
int epoll_create(int size);
  • 功能:创建内核事件监听表
  • 参数:size现在被内核忽略,随便传正数
  • 返回:成功返回epoll的文件描述符epfd;失败返回‑1

2. epoll_ctl

c 复制代码
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 功能:增、删、改内核事件表里的fd
  • op操作选项
    • EPOLL_CTL_ADD:添加fd进事件表
    • EPOLL_CTL_MOD:修改已经注册fd的监听事件
    • EPOLL_CTL_DEL:从事件表删除fd
  • struct epoll_event
c 复制代码
struct epoll_event{
    uint32_t events;    //监听的事件:EPOLLIN读 / EPOLLOUT写 / EPOLLET边沿触发
    epoll_data_t data;  //联合体,保存fd(常用data.fd)或者自定义指针
};
  • 返回值:成功0,失败‑1

3. epoll_wait

c 复制代码
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 功能:阻塞等待事件发生
  • 参数
    1. epfd:epoll实例fd
    2. events:数组首地址,内核把已经就绪的事件拷贝到这个数组
    3. maxevents:events数组最大容量
    4. timeout:超时;‑1代表永久阻塞
  • 返回值
    *

    0:就绪fd的数量nready,只用遍历0 ~ nready‑1

    • -1:出错
    • 0:超时无事件

四、epoll while(1)循环核心逻辑(高频代码考点)

  1. 调用epoll_wait()阻塞等待事件;
  2. 根据返回的nready只循环nready次处理就绪事件;
  3. 判断就绪fd:
    • 如果是监听sockfd:代表新客户端连接,accept()得到confd,用EPOLL_CTL_ADD加入epoll监听;
    • 如果是普通客户端confd:客户端发来数据,执行读写处理函数;
  4. 客户端断开/出错:必须两步:EPOLL_CTL_DEL移除fd + close(confd),防止资源泄漏、CPU死循环;
  5. 处理完成回到循环开头继续epoll_wait等待。

五、select VS epoll对比

  1. select:返回要遍历全部fd;内核修改传入的集合,每次循环要重置集合;fd有上限。
  2. epoll:内核直接把就绪事件给到events数组,只遍历就绪的nready个;不需要每次重置集合;支持海量文件描述符。

六、事件标记

  • EPOLLIN:可读事件(客户端发来数据 / 新连接到来)
  • EPOLLOUT:可写事件
  • EPOLLET:边沿触发模式;默认是水平触发
相关推荐
啊阿狸不会拉杆1 小时前
《计算机网络-自顶向下方法》3.5 面向连接的运输:TCP 读书笔记
网络·tcp/ip·计算机网络
Jason_zhao_MR1 小时前
Linux与采集MCU通信为什么我们用了双SPI
linux·arm开发·单片机·嵌入式硬件·嵌入式
草莓熊Lotso1 小时前
【Redis 初阶】特殊数据类型、渐进式遍历与数据库操作生产指南
linux·网络·数据库·redis·tcp/ip·缓存·bootstrap
λqaq71 小时前
Redis 数据库基础:安装、5 大核心数据类型与常用命令
linux·数据库·redis·python·缓存
lhldsg1 小时前
智慧场馆解决方案软件开发实战:从需求分析到落地部署全流程
数据库·数据仓库·需求分析
huaweichenai1 小时前
spring boot 实现数据库分页操作
数据库·spring boot
CS_Zero1 小时前
Ubuntu 22.04挂载新硬盘
linux·运维·ubuntu
oushaojun21 小时前
深入 Linux 动态链接:so 库构建与运行逻辑(转)
linux
小白羊丨1 小时前
异步任务创建接口如何幂等?
android·数据库