NIO原理浅析(二)

IO分类

阻塞和非阻塞

阻塞IO:用户空间引发内核空间的系统调用,需要内核IO操作彻底完成之后,返回值才会返回到用户空间,执行用户的操作。阻塞指的用户空间程序的执行状态,用户空间程序需要等到IO操作彻底执行完毕。java中,默认创建的socket是阻塞的。

非阻塞IO:用户空间引发内核空间的系统调用,不需要等待内核IO操作彻底完成,内核立即给用户返回一个返回值。用户空间程序继续执行用户的操作,处于非阻塞的状态。java中,设置非阻塞的IO,以socket为例,见如下所示的代码:

serverChannel = ServerSocketChannel.open()
serverChannel.configureBlocking(false)

有兴趣,也可以参考java中关于Socket这个类的文档
在这里插入图片描述

同步和异步

同步IO:用户空间和内核空间的调用发起方式。同步IO是指用户空间的线程是主动发起IO请求的一方,内核空间是被动接受方。

异步IO:用户空间和内核空间的调用发起方式。异步IO是指用户空间的线程是被动接受方,但是内核空间里面的kernel是主动发起IO请求的一方。
在这里插入图片描述

四种常见的IO模型

服务器端的编程需要构造高性能的IO模型,常见的IO模型有四类:

在这里插入图片描述

同步阻塞IO(Blocking IO):

结合上面的描述,同步阻塞IO指的是用户空间主动发起的调用,然后需要等待内核空间将IO操作彻底完成之后才会返回用户空间,这期间,用户空间线程将会处于阻塞状态。
在这里插入图片描述

BIO优点: 程序简单,在阻塞等待数据的期间,用户挂起线程,用户线程基本不会占用CPU资源。

BIO缺点:每个请求可能会配置一套独立的线程,当并发量很高的场景下,内存和线程切换的成本很高。

应用举例:在Java中使用线程池的方式去连接数据库,就是使用的同步阻塞IO模型。

同步非阻塞IO(Non-blocking IO):

如果是socket被设置为non-blocking,NIO模型如果出现了系统调用,会出现以下两种情况:

(1)当内核缓存区里面没有数据,那么当用户空间发起的系统调用时,会立即返回一个失败的信息

(2)当内核缓存区里面有数据,那么当用户空间发起的系统调用时,会进入到阻塞状态,将内核缓存区里面的数据复制到用户缓冲区。直到数据返回成功,才会解除阻塞的状态。

在这里插入图片描述

NIO的优点:每次发起IO系统调用,线程在内核等待缓冲区数据的时候,会立即返回值,不会阻塞。实时性比较好。

NIO的缺点:需要不断地轮询发起系统调用,这样会占用大量的CPU时间,资源利用率很低。

IO多路复用(IO Multiplexing)

首先从字面意思来理解多路复用:

  • 多路: 多个socket网络连接

  • 复用:复用一个线程,使用一个线程来检查多个文件套接字(又称文件句柄)的就绪状态

IO多路复用是一种同步IO模型,实现用一个线程监视多个文件句柄,一旦有文件句柄准备就绪,就可以通知应用程序进行相应的读写操作。没有文件句柄就绪,就会阻塞应用程序,然后交出CPU的时间片。

通过对之前两种IO模型的总结,我们可以发现:

针对高并发的场景,同步阻塞模型的缺点是需要做频繁的内存和线程的切换,效率很低。同步非阻塞的缺点是要在用户程序空间轮询的发起系统调用,这导致内核态和用户态的频繁切换,也会消耗大量的资源。

IO多路复用则可以避免内核态和用户态的频繁切换,因为IO多路复用模型将轮询套接字(又称为文件句柄)的动作,直接放在了内核态进行,这样避免了内核态和用户态的频繁切换

举例说明

我们以基础的socket模型为例,展现IO多路复用的机制:

下面是基础的socket模型伪码:

listenSocket = socket(); //系统调用socket()函数,调用创建一个主动socket
bind(listenSocket); //给主动socket绑定地址和端口
listen(listenSocket); //将默认的主动socket转换成服务器使用的被动socket(也叫监听socket)
while(true) { //循环监听客户端的连接请求connectSocket = accept(listenSocket); //接受客户端连接,获取已连接socketrecv(connSocket); //从客户端读取数据,只能同时处理一个客户端send(connSocket); //给客户端返回数据,只能同时处理一个客户端
}

网络通信的流程如下图所示:
在这里插入图片描述

上图所示的socket网络通信,是典型的同步阻塞模型,当有大量客户端连接时,这种模型的处理性能比较差。使用IO多路复用可以解决这种困境。


linux中,操作系统提供了select、poll和epoll三种多路复用机制。

select机制

四个问题

1、IO多路复用可以最多监听多少个socket?

2、IO多路复用可以监听socket里面的哪些事件?

3、IO多路复用如何感知已经就绪的文件描述符fd?

4、IO多路复用如何实现网络通信?

首先在linux平台上查看一下select函数定义,可以参考一下文章1Linux内核select源码剖析 、文章2Linux select源码分析。

/**
* 参数说明
* 监听的文件描述符数量 __nfds
* 被监听描述符的三个集合*__readfds, *__writefds 和 *__exceptfds
* 监听时阻塞等待的超时时长*__timeout
* 返回值:返回一个socket对应的文件描述符
*/
int select(int __nfds, fd_set * __readfds, fd_set * __writefds, fd_set * __exceptfds, struct timeval * __timeout)

select函数监听的文件描述符被分成三类,分别是__readfds, __writefds 和 __exceptfds,当用户调用select时,假设当前监控的是___readfds集合,select操作会将需要监控___readfds集合从用户空间拷贝到内核空间,随后在内核空间一直遍历自身的skb(SocketBuffer),检查每个skb的poll逻辑,已确定socket是否存在可读事件。若没有socket可读,则会进入到睡眠状态。当发现有sokcet可读,则会唤醒用户空间的程序,然后在用户态去遍历监控的集合,并读取数据。

在这里插入图片描述

select 多路复用方法存在的缺陷:

1、调用select需要将套接字列表从用户态复制到内核态,对于多并发场景,资源消耗量比较大。

2、能监听的端口号的数量有限制,FD_SETSIZE,32位机器限制1024个套接字,64位机器限制2048个套接字。

3、被监控的fdlist列表,如果有一个套接字数据可读,业务就需要遍历一遍用户态的fdlist列表,时间复杂度O(n)。

poll

相较于select,poll优化了select的缺陷二,使用的是pollfd结构,而不是fd_set结构,突破了1024的限制,但是poll没有解决缺陷1和缺陷3,仍然需要导致用户态到内核态的资源消耗过大的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

<Cadence> PCB封装制作(一) 封装组成元素介绍制作表贴焊盘

目录 01 封装的组成元素 02 焊盘Design Layers组成 03 制作表贴焊盘 获取表贴器件(0603电阻)的相关信息 制作表贴器件(0603电阻)焊盘封装 04 文章总结 大家好,这里是程序员杰克。一名平平无奇的嵌入式软件工程师…

java八股文面试[JVM]——类初始化过程

回顾类加载过程: 知识来源: 【2023年面试】Class初始化过程是什么_哔哩哔哩_bilibili

C++day7(auto关键字、lambda表达式、C++中的数据类型转换、C++标准模板库(STL)、list、文件操作)

一、Xmind整理&#xff1a; 关键词总结&#xff1a; 二、上课笔记整理&#xff1a; 1.auto关键字 #include <iostream>using namespace std;int fun(int a, int b, float *c, char d, double *e,int f) {return 12; }int main() {//定义一个函数指针&#xff0c;指向fu…

云备份——第三方库简单介绍并使用(上)

目录 一&#xff0c;Jsoncpp库序列化和反序列化 二&#xff0c;bundle文件压缩库 2.1 文件压缩 2.2 文件解压 一&#xff0c;Jsoncpp库序列化和反序列化 首先我们需要先了解一下json是什么&#xff0c;json是一种数据交换格式&#xff0c;采用完全独立于编程语言的文本格式来…

敏感接口权限校验

前端校验 &#xff08;从前端或者从token里面拿一下&#xff09;&#xff0c;看一下用户有没有这个页面的权限&#xff08;但是一般不用&#xff0c;因为nodejs也可以写后端&#xff0c;但是放到前端去校验不安全&#xff09; 后端校验 需要梳理敏感数据接口&#xff0c;将这…

IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理

IBM Spectrum LSF Explorer 适用于 IBM Spectrum LSF 集群的强大、轻量级报告解决方案 亮点 ● 允许不同的业务和技术用户使用单一解决方案快速创建和查看报表和仪表板 ● 利用可扩展的库提供预构建的报告 ● 自定义并生成性能、工作负载和资源使用情况的报…

FreeSWITCH 1.10.10 简单图形化界面4 - 腾讯云NAT设置

FreeSWITCH 1.10.10 简单图形化界面4 - 腾讯云NAT设置 0、 界面预览1、 查看IP地址2、 修改协议配置3、 开放腾讯云防火墙4、 设置ACL5、 设置协议中ACL&#xff0c;让PBX匹配内外网6、 重新加载SIP模块7、 查看状态8、 测试一下 0、 界面预览 http://myfs.f3322.net:8020/ 用…

【Java Web】敏感词过滤

一、前缀树 假设有敏感词&#xff1a;b&#xff0c;abc&#xff0c;abd&#xff0c;bcd&#xff0c;abcd&#xff0c;efg&#xff0c;hii 那么前缀树可以构造为&#xff1a; 二、敏感词过滤器 package com.nowcoder.community.util;import org.apache.commons.lang3.CharUt…

算法通关村-----哈希和队列的基本知识

哈希概念 哈希也称为散列&#xff0c;就是把任意长度的输入&#xff0c;通过散列算法&#xff0c;变成固定长度的输出&#xff0c;这个输出值就是散列值。 哈希存储 现在有1&#xff0c;2&#xff0c;3…15&#xff0c;要将其存储到大小为7的哈希表中&#xff0c;应该如何存…

OS 死锁处理

如果P先申请mutex 则mutex从1置零&#xff0c;假设申请到的empty 0则empty变成-1阻塞态 同理C中mutex从0变为-1&#xff0c;那么如果想离开阻塞态&#xff0c;那么就需要执行V&#xff08;empty&#xff09;但是如果执行V&#xff08;empty&#xff09;就需要P&#xff08;mu…

什么是跨域(cross-origin)请求,如何解决跨域问题?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 跨域请求和跨域问题⭐ 解决跨域问题的方法1. CORS&#xff08;跨域资源共享&#xff09;2. JSONP&#xff08;JSON with Padding&#xff09;3. 代理服务器4. WebSocket5. 使用服务器中继 ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff…

python爬取bilibili,下载视频

一. 内容简介 python爬取bilibili&#xff0c;下载视频 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接&#xff1a;https://pan.baidu.com/s/1WuXTso_iltLlnrLffi1kYQ?pwd1234 三.主要流程 3.1 下载单个视频 代码 import requests impor…

C# 多线程交替按照指定顺序执行

1.关于AutoResetEvent和ManualResetEvent的区别解释如下&#xff1a; AutoResetEvent和ManualResetEvent是.NET中的两个线程同步类。它们之间的主要区别在于其释放信号的方式以及对等待线程的影响。 AutoResetEvent的作用是在等待的线程被信号唤醒后&#xff0c;将信号自动重…

Flink CDC学习笔记

第一章 CDC简介 1.1 什么是CDC ​ CDC (Change Data Capture 变更数据获取&#xff09;的简称。核心思想就是&#xff0c;检测并获取数据库的变动&#xff08;增删查改&#xff09;&#xff0c;将这些变更按发生的顺序记录下来&#xff0c;写入到消息中间件以供其它服务进行订…

什么是Python爬虫分布式架构,可能遇到哪些问题,如何解决

目录 什么是Python爬虫分布式架构 1. 调度中心&#xff08;Scheduler&#xff09;&#xff1a; 2. 爬虫节点&#xff08;Crawler Node&#xff09;&#xff1a; 3. 数据存储&#xff08;Data Storage&#xff09;&#xff1a; 4. 反爬虫处理&#xff08;Anti-Scraping&…

OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)

目录 1.下载OpenCV的SDK 2.创建Android Native C项目 3.Android项目中导入OpenCV工程 4.导入OpenCV的库文件 5.实现opencv高斯模糊图像处理的demo 要在Android Studio中配置使用OpenCV库的C方法&#xff0c;需要完成以下步骤&#xff1a; 1.下载OpenCV的SDK 首先&#x…

GIT命令只会抄却不理解?看完原理才能事半功倍!

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 系列文章目录一、Git 的特征1. 文件系统2. 分布式 二、GIT的术语1. 区域术语2. 名词术语1. 提交对象2. 分支3. HEAD4. 标签&#xff0…

【python爬虫】6.爬虫实操(带参数请求数据)

文章目录 前言项目&#xff1a;狂热粉丝分析过程什么是带参数请求数据如何带参数请求数据 代码实现被隐藏的歌曲清单什么是Request Headers如何添加Request Headers 复习 前言 先来复习一下上一关的主要知识吧&#xff0c;先热个身。 Network能够记录浏览器的所有请求。我们最…

Go:关于‘fresh‘ 不是内部或外部命令,也不是可运行的程序问题的解决方案

如果你使用了go get命令来安装fresh包&#xff0c;那么fresh命令可能没有被正确添加到系统的PATH环境变量中&#xff0c;需要修改你的fresh.exe的文件存放位置。 一般而言&#xff0c;你会将GO的安装文件夹Go与工作区文件夹GoProjects分开&#xff08;你的文件夹名称与我的不同…