☰
linux笔记归纳23:五种IO模型与非阻塞IO
2026/9/27 22:52:56 网站建设 项目流程

五种IO模型与非阻塞IO

目录

五种IO模型与非阻塞IO

一、五种IO模型

1.1.IO效率问题

1.2.五种IO模型

1.3.同步与异步

1.4.阻塞IO

1.5.非阻塞IO

1.6.信号驱动IO

1.7.多路转接

1.8.异步IO

二、非阻塞IO

2.1.fcntl函数

2.2.实现阻塞与非阻塞读取


一、五种IO模型

1.1.IO效率问题

IO == 等待 + 拷贝

read() → Input缓冲区 → 等待 + 拷贝

write()→ Output缓冲区 → 等待 + 拷贝

单位时间内,传递的数据量越大,IO效率越高

等待时间久,单位时间拷贝量少,IO效率降低

高效IO:单位时间内,减少IO中,等待的比重

1.2.五种IO模型

钓鱼 == 等鱼上钩 + 提鱼进桶 == IO

人 == 进程,鱼 == 数据,鱼竿 == 网络套接字,河 == 操作系统 ,桶 == 用户缓冲区

张三:专注钓鱼,鱼漂不动,他也不动(阻塞IO)

李四:不会因为鱼没有上钩而卡在检测鱼漂上(非阻塞IO)

王五:在鱼竿顶部装上铃铛,鱼上钩反向通知我(信号驱动IO)

赵六:拉一卡车的鱼竿,共100只,在岸边来回等待(多路复用/多路转接)

田七:发起钓鱼过程,让其他人钓鱼,自己干自己的事情(异步IO)

阻塞 VS 非阻塞

IO条件不具备,阻塞就会卡住,直到条件就绪

IO条件不具备,非阻塞检测到后,会出错返回

在钓鱼效率上两者没有任何区别,而非阻塞做了其他工作,效率才高,但IO效率一样

谁的IO效率最高

站在鱼的角度,抬头看 100 / 104 个鱼竿是赵六的

单位时间内,等的比重很低,提高了鱼上钩的效率

王五是否要等待

王五只是不需要检测,但他需要参与钓鱼的过程,需要提鱼进桶

1.3.同步与异步

同步IO:阻塞、非阻塞、信号驱动、多路复用

异步IO:异步IO

同步 VS 异步

IO == 等待 + 拷贝

同步IO:参与IO等待或者拷贝的任意一个或多个阶段

异步IO:只发起IO,但IO的工作流与自身工作流无关

线程同步 VS IO同步

线程同步:多执行流执行具有一定顺序性

IO同步:该执行流是否参与IO的任意过程

1.4.阻塞IO

在内核将数据准备好之前,系统调用一直等待,所有的套接字默认都是阻塞方式

1.5.非阻塞IO

如果内核数据没有准备好,系统调用会直接返回EWOULDBLOCK错误码

非阻塞IO往往需要以循环的方式,反复地尝试读写文件描述符,也称为非阻塞轮询

1.6.信号驱动IO

内核将数据准备好时,使用SIGIO信号通知应用程序进行IO操作

1.7.多路转接

能够同时等待多个文件描述符的就绪状态

传统接口read/write/recv/send/recvfrom/sendto......既负责等待,又负责拷贝

select专门负责等待,数据准备好后,再调用read/write/recv/send/recvfrom/sendto......拷贝

select允许一次等待多个文件描述符

1.8.异步IO

由内核在数据拷贝完成时,通知应用程序

二、非阻塞IO

2.1.fcntl函数

作用:设置一个文件描述符的属性

  • cmd = F_DUPFD:复制一个现有的描述符
  • cmd = F_GETFD/F_SETFD:获得/设置文件描述符标记
  • cmd = F_GETFL/F_SETFL:获得/设置文件状态标记
  • cmd = F_GETOWN/F_SETOWN:获得/设置异步IO所有权
  • cmd = F_GETLK/F_SETLK:获得/设置记录锁

2.2.实现阻塞与非阻塞读取

阻塞IO

#include <iostream> #include <unistd.h> int main() { 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; } //TODO } }

非阻塞IO

#include <iostream> #include <cstdio> #include <unistd.h> #include <fcntl.h> void SetNonBlock(int fd) { int fl = fcntl(fd, F_GETFL); if(fl < 0) { perror("fcntl"); return; } fcntl(fd, F_SETFL, fl | O_NONBLOCK); // 将fd设置为非阻塞 } int main() { SetNonBlock(0); char buffer[1024]; while(true) { // Linux中ctrl + d表示读取结束, read返回值是0, 类似读到文件结尾 ssize_t n = read(0, buffer, sizeof(buffer)); if(n > 0) { buffer[n - 1] = 0; // 去掉回车 std::cout << buffer << std::endl; } else if(n < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK) // 错误码 { // 1. 底层数据没有准备好 std::cout << "数据还未准备好......" << std::endl; sleep(1); // TODO continue; } else if(errno == EINTR) { // 2. 读取被信号中断 continue; } else { // 3. 读取出错 } } else { break; } // sleep(1); // std::cout << ". : " << n << std::endl; // C++也有语言层缓冲区,需要进行刷新才能打印 // TODO } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询