网站首页 > 技术文章 正文
实现层面的网络I/O模型(“Linux 系统)
第一种模型:同步阻塞I/O。
Linux系统的read和write函数,在调用的时候会被阻塞,直到数据读取完成,或者写入成功。
第二种模型:同步非阻塞I/O。和同步阻塞I/O的API是一样的,只是打开fd的时候带有O_NONBLOCK参数。于是,当调用read和write函数的时候,如果没有准备好数据,会理解返回,不会阻塞,然后让应用程序不断地去轮询。
第三种模型:I/O多路复用(IO Multiplexing)。
前面两种I/O都只能用于简单的客户端开发。但对于服务器程序来说,需要处理很多的fd (连接数可以达几十万甚至百万)。
如果使用同步阻塞I/O,要处理这么多的fd需要开非常多的线程,每个线程处理一个 fd;
如果用同步非阻塞 I/O,要应用程序轮询这么大规模的 fd。
这两种办法都不行,所以就有了I/O多路复用。
在Linux系统中,有三种I/O多路复用的办法:select、poll、epoll,它们的原理有一定差异。
该函数是阻塞调用,一次性把所有的 fd 传进去,当有 fd 可读或者可写之后,该函数会返回,返回结果也在这个函数的参数里面,告知应用程序哪些fd上面可读或者可写,然后下一步应用程序调用read和write函数进行数据读写。
I/O多路复用是现在Linux系统上最成熟的网络I/O模型,在三种方式中,epoll的效率最高,所以目前主流的网络模型都是epoll。
第四种模型:异步I/O。Windows系统的IOCP,这是一种真正意义上的异步I/O。
异步I/O,是指读写都是由操作系统完成的,然后通过回调函数或者某种其他通信机制通知应用程序。
C++中的asio网络库。asio是一个跨平台的C++网络库,也是boost的一部分,在Linux系统上封装的是epoll,在Windows系统上封装的是IOCP。asio的接口是完全异步的
async_read/async_write函数传进去的参数主要是三个:·
socket.· 应用程序的buffer· 回调函数。
chat_session 这个类的两个成员函数handle_read_header/handle_write。应用程序调用了这两个函数后都会立即返回,由asio库内部进行I/O读写。读写完成后通过传入的回调函数通知应用程序,读写已经完成。
asio 是一个上层框架层的“异步 I/O”,或者说是模拟出来的“异步I/O”,在 Linux系统上还是由epoll实现的。
“异步”,就是读写由底层完成(操作系统或者框架),读写完成之后,以某种方式通知应用程序。
Linux系统异步I/O的实现是aio。但由于aio并不成熟,所以现在主要还是用epoll。
阻塞与非阻塞
(1)阻塞和非阻塞是从函数调用角度来说的,而同步和异步是从“读写是谁完成的”角度来说的。
阻塞:如果读写没有就绪或者读写没有完成,则该函数一直等待。
非阻塞:函数立即返回,然后让应用程序轮询。
同步:读写由应用程序完成。
异步:读写由操作系统完成,完成之后,回调或者事件通知应用程序。
(2)按照这个定义可以知道,异步I/O一定是非阻塞I/O,不存在既是异步I/O,又是阻塞的;同步I/O可能是阻塞的,也可能是非阻塞的。
归类后共有三种:同步阻塞I/O、同步非阻塞I/O、异步I/O。
(3)I/O多路复用(select、poll、epoll)都是同步I/O,因为read和write函数操作都是应用程序完成的,同时也是阻塞I/O,因为select、read、write的调用都是阻塞的。
事件驱动
Nginx的“事件驱动”,是Nginx封装的一个逻辑概念,在操作系统层面是基于epoll或者select来实现的。
当讲网络 I/O 模型的时候,一定要注意讲的是操作系统层面的 I/O 模型,还是上层的网络框架封装出来的I/O模型(比如asio,又比如Java的NIO,在Linux平台上,底层也是基于epoll)。
对于“异步I/O”一词,在操作系统的语境和在上层应用的语境中,往往指代不一样。
在操作系统的语境里,异步I/O是指IOCP或者aio这种真正的异步,epoll不被认为是异步I/O;但在上层应用的语境里,异步I/O往往指的是JavaJDK或网络框架(Netty)封装出来的概念,底层实现可能是epoll,也可能是真正的异步I/O。
猜你喜欢
- 2024-09-15 C++资深开发工程师带你深入浅出了解Linux后台开发
- 2024-09-15 浅谈linux下C++ 协程与网络编程(linux c++线程同步)
- 2024-09-15 刚学会C++的小白用这个开源框架,做个 RPC 服务要多久?
- 2024-09-15 精选 22 个 C++ 项目,推荐新人练手首选
- 2024-09-15 Node.js 程序员的 C++ 进修指南「1」:SetTimeout
- 2024-09-15 Linux多线程服务端编程 第五章 高效的多线程日志
- 2024-09-15 Linux多线程服务端编程 第七章 muduo 编程示例 后半部分
- 2024-09-15 TCP/IP详解 卷2:实现 第二章 存储器缓存
- 2024-09-15 高性能IO模型分析-Reactor模式和Proactor模式(二)
- 2024-09-15 C++在线五子棋对战(网页版)项目:websocket协议
- 1512℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 556℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 504℃MySQL service启动脚本浅析(r12笔记第59天)
- 482℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 480℃启用MySQL查询缓存(mysql8.0查询缓存)
- 460℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 440℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 438℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)