5种IO模型

后端开发中,网络请求、文件读写都离不开 IO。传统阻塞 IO 只能单线程处理一个请求,无法支撑高并发;而 IO 多路复用、异步 IO 是 Redis、Nginx、Netty 等高性能组件的核心。不懂 IO 模型,就无法理解高并发服务器的设计思想。

1:IO本质

所有IO(网络/文件)都只有两个核心步骤,这是理解IO模型的根基:

1:等待数据就绪:内核等待数据从网卡/硬盘写入内核缓冲区

2:数据拷贝:内核将数据从内核空间拷贝到用户空间

IO优化的核心:尽可能减少等待时间

2:五种IO模型

钓鱼帮你秒懂,对应专业定义 + 流程:

1. 阻塞 IO(最基础)

  • 通俗:坐在河边死等鱼上钩,啥也不干,直到鱼上钩才拉杆
  • 专业:内核数据没准备好,进程一直阻塞,直到数据拷贝完成才返回
  • 特点:最简单、默认模式、CPU 利用率低、单线程只能处理一个 IO

2. 非阻塞 IO

  • 通俗:每隔几秒看一眼鱼竿,没鱼立刻返回,去干别的,轮询检查
  • 专业:数据没准备好直接返回EWOULDBLOCK,进程循环轮询
  • 特点:不阻塞、CPU 浪费严重、仅特定场景用

3. 信号驱动 IO

  • 通俗:鱼竿装铃铛,鱼上钩内核发信号,收到信号再去拉杆
  • 专业:内核数据就绪,发送SIGIO信号通知进程,进程再发起 IO
  • 特点:不用轮询、通知后仍需主动拷贝数据

4. IO 多路复用(高并发核心)(后面重点)

  • 通俗:雇人看100 根鱼竿,哪个有鱼喊你,你只负责拉杆
  • 专业:用select/poll/epoll同时监听多个文件描述符,任一就绪就处理
  • 特点:单线程处理百万连接、网络服务器标配

5. 异步 IO(最理想)

  • 通俗:雇人直接把鱼钓好送到你家,你全程不用管
  • 专业:内核完成等待 + 拷贝全流程,完成后通知进程直接用数据
  • 特点:全程无阻塞、实现复杂、Linux 支持有限

3:知识层级

1:什么层的知识

操作系统内核层 + 网络应用层 / 传输层

  • 不属于硬件层、语言层;是OS 内核提供的 IO 调度机制
  • 网络 IO 基于Socket 套接字 (传输层 TCP/UDP),是网络编程的底层基石

2:在OS/网络中扮演的角色

  • 操作系统:管理进程 IO 状态、内核缓冲区、用户 / 内核空间数据拷贝、调度 IO 流程
  • 网络编程 :决定服务器并发能力,是高并发 Web 服务器 / 网关 / 中间件的核心底层

3:和语言层的关系

和编程语言无关!语言只做「封装」,不创造 IO 模型

  • C++/Java/Python/Go:底层都是调用Linux 系统调用recvfrom/select/fcntl
  • 语言差异:只是封装了 OS 的 IO 能力(Java NIO、Python asyncio、Go net 包)
  • 跨平台:Windows 用 IOCP,Linux 用 epoll,API 不同但 IO 模型思想一致

4:三种IO实战编程(C++和Linux环境)

1:阻塞IO

cpp 复制代码
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
    char buf[1024]={0};
    cout<<"阻塞IO:请输入内容(不输入就一直等待):"<<endl;
    //阻塞:无输入时进程挂起
    ssize_t n = read(0,buf,sizeof(buf));
    if(n>0) cout<<"读取内容"<<buf<<endl;
    return 0;
}

2:非阻塞IO(fcntl设置)

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
using namespace std;

// 设置文件描述符为非阻塞
void setNonBlock(int fd) {
    int flag = fcntl(fd, F_GETFL);       // 获取当前属性
    fcntl(fd, F_SETFL, flag | O_NONBLOCK); // 添加非阻塞标记
}

int main() {
    setNonBlock(0); // 标准输入0号描述符设为非阻塞
    char buf[1024] = {0};
    cout << "非阻塞IO:轮询检查输入..." << endl;

    while(true) {
        ssize_t n = read(0, buf, sizeof(buf)-1);
        if(n < 0) {
            // 无数据:返回EAGAIN/EWOULDBLOCK,继续轮询
            if(errno == EAGAIN || errno == EWOULDBLOCK) {
                cout << "暂无数据,1秒后重试..." << endl;
                sleep(1);
                continue;
            }
        }
        if(n > 0) {
            cout << "读取内容:" << buf << endl;
            break;
        }
    }
    return 0;
}

3:IO多路复用(select实现)

cpp 复制代码
//select入门
#include <iostream>
#include <sys/select.h>
#include <unistd.h>
using namespace std;

int main()
{
    //1.定义监听集合
    fd_set read_set;
    while(true)
    {
        //步骤1:清空集合+加入要监听的fd(0=标准输入)
        FD_ZERO(&read_set);
        FD_SET(0, &read_set);//监听标准输入

        //步骤2:调用select函数,阻塞等待fd就绪
        //参数:最大fd+1,读集合,写集合,异常集合,超时时间(nullptr=永久等待)
        //返回值:就绪fd的数量,-1表示出错,0表示超时
        int ret=select(1,&read_set,nullptr,nullptr,nullptr);

        if(ret<0)
        {
            cerr<<"select error!"<<endl;
            break;
        }
        if(FD_ISSET(0,&read_set))//步骤3:判断哪个fd就绪了+处理数据
        {
            char buf[1024]={0};
            ssize_t n=read(0,buf,sizeof(buf)-1);
            if(n>0)//成功读取数据
            {
                buf[n]='\0';
                cout<<"read from stdin:"<<buf<<endl;
            }
        }
    }
    return 0;
}
相关推荐
YaBingSec2 小时前
玄机靶场-2024ccb初赛sc05 WP
android·运维·网络·笔记·安全·ssh
Andytoms2 小时前
小桔调研:3分钟Docker搭建问卷系统
运维·docker·容器
mounter6252 小时前
【内核精进】Linux Kernel 设计模式(一):引用计数与可见性的艺术
linux·设计模式·linux kernel
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【删数问题】:删数问题2
c++·算法·贪心·csp·信奥赛
.千余2 小时前
【Linux】开发工具2:vim
linux·服务器·开发语言·学习
SWAGGY..2 小时前
【C++初阶】:(10)vector的使用及模拟实现
开发语言·c++
坚持就完事了2 小时前
再谈编辑器Vim
linux·编辑器·vim
SariHcr1232 小时前
Openarm机器人双臂模型仿真从零部署
c++·人工智能·python·机器人·bash·openarm
故事还在继续吗2 小时前
C++11关键特性
开发语言·c++·算法