【Linux网络编程】11. 五种 IO 模型与非阻塞 IO
·
文章目录
一、五种 IO 模型
1、简要理解
这里以一个钓鱼的例子来说明五种IO模型:
- 张三:专注钓鱼,鱼漂不动,张三不动 — 阻塞 IO
- 李四:不会因为鱼没有上钩而卡在检测鱼漂上 ---- 非阻塞 IO
- 王五:通过让鱼上钩,反向通知我 — 信号驱动 IO
- 赵六:拉了一卡车的鱼竿 (100 只鱼竿) ---- 多路复用,多路转接
- 田七:我是喜欢吃鱼!发起钓鱼,让小王去钓鱼 — 异步 IO
问题 1:阻塞 vs 非阻塞
- 阻塞:IO 条件不具备时,一直卡住,直到条件就绪。
- 非阻塞:IO 条件不具备时,立即出错返回,不阻塞等待。
- 核心差异:等待方式不同,非阻塞可同时做其他事。
问题 2:谁的钓鱼效率最高?
多路复用(赵六)效率最高,因为单位时间内等待的占比最低。
问题 3:信号驱动 IO(王五)有没有等待?
有参与钓鱼(IO)流程,只是不用主动检测条件。
结论 4:同步 IO 模型
阻塞、非阻塞、信号驱动、多路复用,都属于同步 IO。
只要用户参与了「等待」或「拷贝」任一阶段,就是同步 IO。
问题 5:同步 IO vs 异步 IO
- 同步 IO:用户参与「等待」或「拷贝」任一阶段。
- 异步 IO:用户只发起 IO,等待和拷贝全由内核完成,与用户流程无关。
2、阻塞 IO
核心:IO 分等待数据和拷贝数据两步,不同模型的差异主要在于等待数据
- 流程:调用
recvfrom后一直阻塞,直到数据准备好并拷贝完成才返回。 - 特点:最简单,默认方式,但一个线程只能处理一个 IO,等待期间无法做其他事。
3、非阻塞 IO
- 流程:调用
recvfrom时数据没准备好会立刻返回EWOULDBLOCK错误,需要反复轮询直到成功。 - 特点:不阻塞,但轮询会浪费 CPU 资源,一般很少直接用。
4、信号驱动 IO
- 流程:提前注册
SIGIO信号处理函数,数据准备好时内核发信号通知,再调用recvfrom拷贝数据。 - 特点:等待期间进程可做其他事,但信号处理和多线程场景容易有竞态问题。
5、IO 多路转接(多路复用)
- 流程:通过
select/poll/epoll同时等待多个文件描述符,任意一个就绪后再调用recvfrom处理。 - 特点:单线程可同时管理多个 IO,效率高,是高并发服务的主流方案。
6、异步 IO
- 流程:调用
aio_read后立刻返回,内核负责等待 + 拷贝数据,全程不阻塞,拷贝完成后发信号通知应用。 - 特点:完全异步,效率最高,但实现复杂,依赖系统支持。
二、高级 IO 重要概念
1、同步通信 vs 异步通信
-
同步通信
- 定义:调用发出后,必须主动等待结果返回,调用不返回就一直阻塞,拿到结果才继续执行。
- 特点:调用者全程等结果,逻辑简单但效率低。
-
异步通信
- 定义:调用发出后立刻返回,不阻塞等待结果;后续由被调用者通过 状态通知 / 回调函数,主动把结果反馈给调用者。
- 特点:调用者可继续做其他事,效率高但逻辑更复杂。
-
注意事项
- IO / 通信场景:指同步 / 异步通信,描述的是消息通知机制;
- 多进程 / 线程场景: 指同步 / 互斥,描述的是多任务之间的执行顺序、资源访问制约关系,和前者完全无关。
2、阻塞 vs 非阻塞
- 阻塞调用: 调用结果未返回前,当前线程会被挂起(无法执行其他操作),直到拿到结果才恢复运行。
- 非阻塞调用: 即使暂时拿不到结果,调用也会立刻返回,线程不会被挂起,可继续执行其他操作(通常需要后续轮询获取结果)。
总结: 阻塞是 “调用时死等”,非阻塞是 “调用时不等,能继续做别的事”
3、高级 IO
除基础 IO 外,以下这些统称为高级 IO:
- 非阻塞 IO
- 记录锁
- 系统 V 流机制
- IO 多路转接(IO 多路复用,如 select/poll/epoll)
- readv/writev 分散 - 聚集 IO
- 存储映射 IO(
mmap)
4、非阻塞 IO
- 文件描述符默认是阻塞 IO。
- 使用
fcntl()函数可将其设置为非阻塞。
1)fcntl 函数
2)实现函数 SetNoBlock
作用:把文件描述符设为非阻塞 IO
void SetNoBlock(int fd)
{
// 1. 获取当前文件状态标记
int f1 = fcntl(fd, F_GETFL);
if(f1<0)
return;
// 2. 加上非阻塞标记, 重新设置
fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}
3)轮询方式读取标准输入
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
// 设置文件描述符为非阻塞
void SetNoBlock(int fd)
{
int f1 = fcntl(fd, F_GETFL);
if(f1<0)
return;
fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}
int main()
{
SetNoBlock(0); // 标准输入非阻塞
char buffer[1024];
while (true)
{
ssize_t n = read(0, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n - 1] = 0;
std::cout << buffer << std::endl;
}
else if(n==0)
{
break;
}
else
{
// 无数据/被信号打断 → 继续轮询
if (errno == EAGAIN || errno == EINTR)
{
std::cout << "数据未就绪..." << std::endl;
sleep(1);
}
else
{
break; // 真错误
}
}
}
return 0;
}

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)