
友友们,早上好,经常使用linux的朋友应该都知道在 Linux 系统的 IO 操作中,不同场景对效率、资源占用的需求差异显著,选择合适的 IO 模型直接决定程序的性能上限。今天我们就聊聊关于Linux 下五种核心 IO 模型的原理、流程及适用场景,并结合代码实例来讲解非阻塞 IO 与 IO 多路转接的实践应用,ok,下面正文开始。
任何 IO 操作的本质都包含 “等待数据就绪” 和 “数据拷贝” 两个阶段,不同模型的核心差异在于 “等待方式” 的优化 —— 让等待时间最小化,是提升 IO 效率的关键。下面我们来看一下这5种io的原理。
总结一张思维导图,方便大家理解

文件描述符默认是阻塞模式,需通过fcntl函数修改为非阻塞模式,以下是完整实现案例。
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
using namespace std;
// 将文件描述符设置为非阻塞模式
void SetNoBlock(int fd) {
// 获取当前文件状态标志
int fl = fcntl(fd, F_GETFL);
if (fl < 0) {
perror("fcntl F_GETFL error");
return;
}
// 保留原有标志,添加非阻塞标志O_NONBLOCK
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}int main() {
// 将标准输入(fd=0)设置为非阻塞
SetNoBlock(0);
char buf[1024] = {0};
while (1) {
// 尝试读取标准输入
ssize_t n = read(0, buf, sizeof(buf) - 1);
if (n > 0) {
// 去除换行符,添加字符串结束符
buf[n - 1] = '\0';
cout << "读取到数据:" << buf << endl;
} else if (n < 0) {
// 区分错误类型
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 无数据可用,避免CPU空转,休眠1秒
cout << "暂无输入,等待中..." << endl;
sleep(1);
} else if (errno == EINTR) {
// 系统调用被信号中断(如Ctrl+C),忽略并继续
continue;
} else {
// 其他错误(如fd无效),打印错误并退出
perror("read error");
break;
}
} else {
// n == 0 表示读取到文件结尾(Linux中Ctrl+D触发)
cout << "输入结束,退出程序" << endl;
break;
}
}
return 0;
}select是 IO 多路转接的经典实现,支持同时监控多个 fd 的可读、可写、异常状态,是入门 IO 多路转接的最佳选择。
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
// 返回值:就绪的fd数量;0=超时;-1=出错
int select(
int nfds, // 监控的最大fd + 1
fd_set *readfds, // 可读fd集合(NULL表示不监控)
fd_set *writefds, // 可写fd集合(NULL表示不监控)
fd_set *exceptfds, // 异常fd集合(NULL表示不监控)
struct timeval *timeout // 超时时间(NULL=阻塞,{0,0}=非阻塞)
);fd_set是位图结构(比特位对应 fd 编号),需通过以下宏操作管理:
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main() {
fd_set readfds;
// 超时时间设置为3秒(tv_sec=秒,tv_usec=微秒)
struct timeval timeout = {3, 0};
while (1) {
// 1. 每次调用前需重新初始化集合(select会修改集合内容)
FD_ZERO(&readfds);
// 2. 将标准输入(fd=0)加入可读监控集合
FD_SET(0, &readfds);
// 3. 调用select监控,最大fd=0,故nfds=0+1=1
int ready = select(1, &readfds, NULL, NULL, &timeout);
if (ready < 0) {
perror("select error");
break;
} else if (ready == 0) {
printf("超时3秒,未检测到输入\n");
// 超时后需重新设置timeout(select会修改timeout值)
timeout.tv_sec = 3;
timeout.tv_usec = 0;
continue;
}
// 4. 检查标准输入是否就绪,就绪则读取数据
if (FD_ISSET(0, &readfds)) {
char buf[100] = {0};
ssize_t n = read(0, buf, sizeof(buf) - 1);
if (n > 0) {
printf("你输入的内容:%s", buf);
}
}
// 重置超时时间
timeout.tv_sec = 3;
timeout.tv_usec = 0;
}
return 0;
}当然了,对于我们开发者来说,IO 模型的选择没有绝对最优解,需结合业务场景的并发量、响应要求、开发成本综合判断 —— 理解 “等待” 与 “拷贝” 的优化逻辑,才能精准匹配技术方案,实现 IO 效率的最大化。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。