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;
}
相关推荐
杜子不疼.9 小时前
【 C++ AI 大模型接入 SDK】 - 日志模块
开发语言·javascript·c++
3Tony9 小时前
解决VScode报错:preLaunchTask“C/C++: gcc.exe 生成活动文件“已终止,退出代码为 -1.
c++·ide·vscode
学术小李9 小时前
VSCode上传文件到服务器
服务器·vscode·sftp·数据上传
hweiyu009 小时前
Linux命令:nmtui
linux
MY_TEUCK9 小时前
【2026最新Linux虚拟机安装】Linux 虚拟机安装VMware 17 + CentOS 7
linux·运维·centos
C+++Python9 小时前
C++ 泛型编程 极简示例代码
开发语言·c++
Rust研习社9 小时前
Ubuntu 全面拥抱 Rust 后,我意识到 Rust 社区要变了
linux·服务器·开发语言·后端·ubuntu·rust
Shingmc39 小时前
【Linux】传输层协议TCP
linux·网络·tcp/ip
宵时待雨10 小时前
回溯算法专题2:二叉树中的深搜
开发语言·数据结构·c++·笔记·算法·深度优先
再战300年10 小时前
nginx之负载均衡
运维·nginx·负载均衡