五种网络IO模型

news2024/11/21 19:55:04

五种模型出自:RFC标准。可参考: 《UNIX网络编程-卷一》 6.2

很多程序员是从高级语言的网络编程/文件操作了解到nio,继而了解到五种io模型的;

这五种io模型不止用于网络io

  • “阻塞与****系统调用”是怎么回事?我知道了线程.sleep()可以阻塞线程,指定sleep位可以标记状态,,但是还没见到阻塞的系统调用,,
  • 是说发动调用后阻塞?调用系统调用自动阻塞?
  • 说到底系统调用是怎么用啊,原以为是包含头文件,结果头文件是封装好的入口,,

linux网络编程一共有几种 IO 模型?NIO 和多路复用的区别?

一共有五种IO模型

  • 阻塞IO模型 BIO
  • 非阻塞IO模型 NIO
  • IO多路复用模型(select,poll,epoll…)
  • 信号驱动模型(SIGIO)
  • 异步IO(AIO,POSIX的aio_系列函数) Future-Listener机制 )

IO操作可分为两阶段看待:

1)进程向发起IO请求,等待数据准备(Waiting for the data to be ready),系统调用后进入内核态,内核操作数据到内核缓冲区

2)实际的IO操作,将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

I/O多路复用是阻塞在select,epoll这样 的系统调用没有阻塞在真正的I/O系统调用

如recvfrom进程受阻于select,等待可能多个套接口中的任一个变为可读

IO多路复用使用两个系统调用(select和 recvfrom) ,blocking IO只调用了一个系统调用 (recvfrom)

多路复用模型中,每一个socket,设置为 non-blocking, 阻塞是被select()阻塞,而不是被 socket阻塞的

select/epoll 核心是可以同时处理多个 connection,而不是更快的处理单个connection,所以连接数不高的话,性能不一定比多线程+阻塞IO好

前四种IO模型都是同步IO操作,他们的区别在于第一阶段,而第二阶段是一样的,即在内核数据copy到用户空间时都是阻塞的。

而异步 I/O模型的进程在这两个阶段都是运行的。

阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞。

同步IO和异步IO的区别就在于第二个步骤是否阻塞,实际IO读写阶段会阻塞进程,而在异步IO中,是由操作系统帮忙做完IO工作再直接返回结果。

  • 同步是在等待什么?阻塞是通知进程后等他主动发起吗?进程是在空转等着通知,还是干着其他事情,轮询着等通知吗?

几个核心点:

  • 此处阻塞非阻塞说的是线程的状态,同步和异步说的是消息的通知机制
  • 同步需要主动读写数据,异步是不需要主动读写数据
  • 同步IO和异步IO是针对用户应用程序和内核的交互

##NIO(非阻塞IO模型)

NIO,即Non-Blocking IO,是非阻塞IO模型。

NIO存在性能问题,即频繁的轮询,导致频繁的系统调用,同样会消耗大量的CPU资源。可以考虑IO复用模型去解决这个问题。

不阻塞了,但是轮询doge

阻塞会让出cpu,轮询会一直占着cpu

非阻塞IO的流程如下:

图片

  1. 应用进程发起IO系统调用,内核态进行IO操作
  2. 应用进程轮询向操作系统内核,发起recvfrom读取数据。
  3. 操作系统内核数据没有准备好,立即返回EWOULDBLOCK错误码。
  4. 应用程序进程轮询调用,继续向操作系统内核发起recvfrom读取数据。
  5. 操作系统内核数据准备好了,从内核缓冲区拷贝到用户空间。
  6. 完成调用,返回成功提示。

对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

常见的RPC框架,如Thrift,Dubbo

这种框架内部一般维护了请求的协议和请求号,可以维护一个以请求号为key,结果的result为future的map,结合NIO+长连接,获取非常不错的性能。

IO多路复用模型

多路指多个TCP连接(即 socket或者channel),复用指复用一个或几个线程。

解决轮询的方法:先阻塞进程,等到内核数据准备好了,主动通知应用进程再去进行系统调用。

  • 阻塞等于挂起吗?是说发起select()调用后,进程被阻塞,进入内核态等select调用返回?既然说阻塞与select()调用,那应该是这个意思。

IO复用模型核心思路:系统给我们提供一类函数(如select、poll、epoll函数),它们可以同时监控多个fd的操作,任何一个返回内核数据就绪,应用进程再发起recvfrom系统调用。

  • 多个Socket怎么说?等一批一起处理?后面追加的怎么办?

最简单的Reactor模式:注册所有感兴趣的事件处理器,单线程轮询选择就绪事件,执行事件处理器。

Java的Selector对于Linux系统来说,有一个致命限制:同一个channel的select不能被并发的调用。因此,如果有多个I/O线程,必须保证:一个socket只能属于一个IoThread,而一个IoThread可以管理多个socket。

另外连接的处理和读写的处理通常可以选择分开,这样对于海量连接的注册和读写就可以分发。虽然read()和write()是比较高效无阻塞的函数,但毕竟会占用CPU,如果面对更高的并发则无能为力。img

对于Redis来说,由于服务端是全局串行的,能够保证同一连接的所有请求与返回顺序一致。这样可以使用单线程+队列,把请求数据缓冲。然后pipeline发送,返回future,然后channel可读时,直接在队列中把future取回来,done()就可以了。

常见的RPC框架,如Thrift,Dubbo

这种框架内部一般维护了请求的协议和请求号,可以维护一个以请求号为key,结果的result为future的map,结合NIO+长连接,获取非常不错的性能。

IO多路复用之select

应用进程通过调用select函数,可以同时监控多个fd,在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。

图片

NIO中,需要轮询多次轮询系统调用直到可以读取数据,然而借助select的IO多路复用模型,只需要发起一次询问就够了,大大优化了性能。

select监视文件3类描述符: writefds、readfds、和 exceptfds。

调用后select函数会阻塞住,等有数据 可读、可写、出异常 或者 超时 就会返回。

但是,select有几个缺点:

  • 单个进程监听的IO最大连接数(FD,文件描述符)有限,默认是1024 (可修改宏定义) static final int MAX_FD = 1024
  • select函数返回后,是通过遍历fdset,找到就绪的描述符。(仅知道有I/O事件发生,却不知是哪几个,所以遍历所有来查找(不能随机访问的数据结构的亚子))随着数量增加而性能下降
  • 每次调用 select(),需要把 fd 集合从用户态拷贝到 内核态,并进行遍历(消息传递都是从内核到用户空间

poll

因为存在连接数限制,所以后来又提出了poll。与select相比,poll解决了连接数限制问题(用链表存储)。但是select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。

IO多路复用之epoll

为了解决select/poll存在的问题,多路复用模型epoll诞生,它采用事件监听回调机制来实现,流程图如下:

图片

epoll先通过epoll_ctl()来注册一个fd,一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd,当进程调用epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。

epoll()在2.6内核中提出的,对比select和poll,epoll更加灵 活,没有描述符限制,用户态拷贝到内核态只需要使用事件通知

优点:

  1. 没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个句柄

  2. 效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降。通过callback机制通知,内核和用户空间mmap同一块内存实现

JAVA中的I/O

1.4之前BIO

大型服务一般采用 C或者C++, 因为可以直接操作系统提供的异步IO,AIO.

NIO @Since(“1.4”)

NIO2.0 @Since(“1.7”),提供 AIO的功能,支持文件和网络套接字的异步IO.

NIO高级

Proactor与Reactor

一般情况下,I/O 复用机制需要事件分发器(event dispatcher)。 事件分发器的作用,即将那些读写事件源分发给各读写事件的处理者,就像送快递的在楼下喊: 谁谁谁的快递到了, 快来拿吧!开发人员在开始的时候需要在分发器那里注册感兴趣的事件,并提供相应的处理者(event handler),或者是回调函数;事件分发器在适当的时候,会将请求的事件分发给这些handler或者回调函数。

涉及到事件分发器的两种模式称为:Reactor和Proactor。Reactor模式是基于同步I/O的,而Proactor模式是和异步I/O相关的。

Reactor相当于去医院,先用事件分发器向操作系统挂号,等叫到号了(被通知can read 或者 can write)应用线程再去实际IO

Proactor相当于网上办理银行业务,向操作系统告知自己要把数据调动到什么地方,然后等操作系统内核线程完成了再告诉事件分发器

有点类似有DMA的硬件设备的IO,这里的NIO是面向线程的

在Reactor模式中,事件分发器等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。

而在Proactor模式中,事件处理者(或者代由事件分发器发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区、读的数据大小或用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分发器得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。举例来说,在Windows上事件处理者投递了一个异步IO操作(称为overlapped技术),事件分发器等IO Complete事件完成。这种异步模式的典型实现是基于操作系统底层异步API的,所以我们可称之为“系统级别”的或者“真正意义上”的异步,因为具体的读写是由操作系统代劳的。

在Proactor中实现读:

  • 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
  • 事件分发器等待操作完成事件。
  • 在分发器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分发器读操作完成。
  • 事件分发器呼唤处理器。
  • 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分发器。

附录

相关系统调用

linux

  • recvfro
  • epoll_create() 在Linux内核里面申请一个文件系统 B+树,返回epoll对象,也是一个fd
  • epoll_ctl() 操作epoll对象,在这个对象里面修改 添加删除对应的链接fd, 绑定一个callback函数
  • epoll_wait() 判断并完成对应的IO操作

windows

  • IOCP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/903145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis之持久化机制

文章目录 一、redis持久化二、持久化方式2.1. RDB方式2.1.1 RDB手动2.1.2 RDB自动2.1.3RDB优缺点 2.2AOF方式2.2.1 AOF写数据遇到的问题2.2.2 AOF重写方式 二、RDB和AOF优缺点对比总结 一、redis持久化 Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘&a…

Azure防火墙

文章目录 什么是Azure防火墙如何部署和配置创建虚拟网络创建虚拟机创建防火墙创建路由表,关联子网、路由配置防火墙策略配置应用程序规则配置网络规则配置 DNAT 规则 更改 Srv-Work 网络接口的主要和辅助 DNS 地址测试防火墙 什么是Azure防火墙 Azure防火墙是一种用…

ELK日志监控系统搭建docker版

目录 日志来源elk介绍elasticsearch介绍logstash介绍kibana介绍 部署elasticsearch拉取镜像:docker pull elasticsearch:7.17.9修改配置⽂件:/usr/share/elasticsearch/config/elasticsearch.yml启动容器设置密码(123456)忘记密码…

Redis从基础到进阶篇(一)

目录 一、了解NoSql 1.1 什么是Nosql 1.2 为什么要使用NoSql 1.3 NoSql数据库的优势 1.4 常见的NoSql产品 1.5 各产品的区别 二、Redis介绍 2.1什么是Redis 2.2 Redis优势 2.3 Redis应用场景 2.4 Redis下载 三、Linux下安装Redis 3.1 环境准备 3.2 Redis的…

Win11右键显示更多选项

不需要重启电脑,重启资源管理器即可,用命令:taskkill /f /im explorer.exe & start explorer.exe

一、Kafka概述

目录 1.3 Kafka的基础架构 1.3 Kafka的基础架构 Producer:消息生产者,就是向 Kafka broker 发消息的客户端Consumer:消息消费者,向 Kafka broker 取消息的客户端。Consumer Group(CG):消费者组&…

浅析深浅拷贝

我们在对对象进行复制时就用到深浅拷贝。 一、普通复制 <script>const people{name:tim,age:22}const testpeople;console.log(test);//tim 22test.age20;console.log(test);//tim 20console.log(people);//tim 20 </script> 控制台打印结果&#xff1a; 之所以…

使用struct解析通达信本地Lday日线数据

★★★★★博文原创不易&#xff0c;我的博文不需要打赏&#xff0c;也不需要知识付费&#xff0c;可以白嫖学习编程小技巧&#xff0c;喜欢的老铁可以多多帮忙点赞&#xff0c;小红牛在此表示感谢。★★★★★ 在Python中&#xff0c;struct模块提供了二进制数据的打包和解包…

使用transformers生成文本Generating text with transformers

到目前为止&#xff0c;您已经看到了Transformers架构内部的一些主要组件的高级概述。但您还没有看到从头到尾的整体预测过程是如何工作的。让我们通过一个简单的例子来了解。在这个例子中&#xff0c;您将查看一个翻译任务或一个序列到序列的任务&#xff0c;这恰好是Transfor…

破解难题:如何应对项目中的‘老油条’障碍

引言 在项目管理的实践中&#xff0c;我们经常遇到各种各样的人员挑战。其中&#xff0c;有一种特殊的挑战被称为“老油条”现象。这些“老油条”通常在表面上表现得非常配合&#xff0c;但在实际工作中却常常没有任何进展。这种情况不仅会影响项目的进度&#xff0c;还可能对…

机器学习---常见的距离公式(欧氏距离、曼哈顿距离、标准化欧式距离、余弦距离、杰卡德距离、马氏距离、切比雪夫距离、闵可夫斯基距离、K-L散度)

1. 欧氏距离 欧几里得度量&#xff08;euclidean metric&#xff09;&#xff08;也称欧氏距离&#xff09;是一个通常采用的距离定义&#xff0c;指在m维空 间中两个点之间的真实距离&#xff0c;或者向量的自然长度&#xff08;即该点到原点的距离&#xff09;。在二维和三维…

Spring(16) Aware结尾的类整理

目录 一、什么是 Aware 结尾的类&#xff1f;二、常见的 Aware 实现接口三、Aware 实现原理 一、什么是 Aware 结尾的类&#xff1f; 在 Spring Boot 中&#xff0c;以 Aware 结尾的类通常是一些继承了 Aware 接口的接口类&#xff0c;它们用于使 Bean 获取某些特定的能力或资…

AJAX的POST请求在chrome浏览器报net::ERR_CONNECTION_RESET问题

背景说明 公司对前端的所有的AJAX请求做了统一的封装&#xff0c;因此业务上需要发起请求调用后端服务时&#xff0c;使用的都是公司封装好的工具。 由于ERR_CONNECTION_RESET问题比较粗&#xff0c;也就是说可能会有很多原因会导致浏览器报这个错&#xff0c;因此在网上可以…

clion软件ide的安装和环境配置@ubuntu

1.官网&#xff1a; Download CLion 2.安装Clion 直接在官网下载并安装即可&#xff0c;过程很简单 https://www.jetbrains.com/clion/ https://www.jetbrains.com/clion/download/#sectionlinux 3.激活码 4.配置Clion 安装gcc、g、make Ubuntu中用到的编译工具是gcc©…

Java面向对象——多态、Object类、instanceof关键字以及final关键字

多态的概念 1.多态是指同一个方法调用可以在不同的对象上有不同的表现&#xff0c;即同一种方法调用方式适用于不同的数据类型。 编译时和运行时&#xff1a;编译时期调用的是父类中的方法&#xff0c;但运行时期会根据实际的对象类型来调用适当的方法。这种行为称为动态绑定&…

自注意力机制简介Transformers: Attention is all you need

“Attention is All You Need” 是一篇由Google研究人员在2017年发表的研究论文&#xff0c;该论文介绍了Transformer模型&#xff0c;这是一种革命性的架构&#xff0c;它彻底改变了自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并成为我们现在所知道的LLMs的基础…

剪枝基础与实战(1): 概述

本文介绍基于L1正则化的剪枝原理,并以VGG网络进行实战说明。将从零详细介绍模型训练、稀疏化、剪枝、finetune的全过程,提供详细的源码及说明,有助于对剪枝的熟练掌握,后续也会对yolov8进行剪枝的介绍。 论文: Learning Efficient Convolutional Networks through Network …

学习笔记|基于Delay实现的LED闪烁|模块化编程|SOS求救灯光|STC32G单片机视频开发教程(冲哥)|第六集(下):实现LED闪烁

文章目录 2 函数的使用1.函数定义&#xff08;需要带类型&#xff09;2.函数声明&#xff08;需要带类型&#xff09;3.函数调用 3 新建文件&#xff0c;使用模块化编程新建xxx.c和xxx.h文件xxx.h格式&#xff1a;调用头文件验证代码调用&#xff1a;完整的文件结构如下&#x…

PyTorch学习笔记(十七)——完整的模型验证(测试,demo)套路

完整代码&#xff1a; import torch import torchvision from PIL import Image from torch import nnimage_path "../imgs/dog.png" image Image.open(image_path) print(image)# 因为png格式是四个通道&#xff0c;除了RGB三通道外&#xff0c;还有一个透明度通…