Unix I/O 模型及Java I/O 模型详解

在Unix Socket的输入操作中,可以将其分为以下几个阶段:

  1. 等待数据就绪(内核空间)
    在这个阶段,应用程序通过调用阻塞式的读取函数(如recv)或非阻塞式的读取函数(如recv、recvfrom)等待数据的到达。如果没有数据到达,阻塞式的读取函数会一直等待,而非阻塞式的读取函数会立即返回一个错误码或标识表示数据未准备好。

  2. 数据拷贝到内核缓冲区(内核空间)
    当数据就绪后,操作系统会将数据从网络中拷贝到内核缓冲区中。这个阶段是在内核空间中进行的,应用程序无法直接访问或操作内核缓冲区。

  3. 数据拷贝到用户缓冲区(用户空间)
    在这个阶段,从内核缓冲区将数据拷贝到应用程序提供的用户缓冲区中。这个步骤涉及了将数据从内核空间切换到用户空间的操作。

  4. 数据处理和应用程序操作(用户空间)
    一旦数据被拷贝到用户缓冲区,应用程序就可以对数据进行处理和操作。这可能包括解析数据、执行特定的业务逻辑或对数据进行进一步的处理

在整个输入操作过程中,数据从网络到达应用程序的用户空间,经过了多次拷贝和处理。故而产生零拷贝的方案,以减少用户空间与CPU内核空间的拷贝过程,减少用户上下文与CPU内核上下文间的切换,提高系统效率

不同的IO模型可能会对输入操作的阶段和执行方式有所影响。例如,阻塞IO模型和非阻塞IO模型在等待数据就绪阶段的行为上有差异,而异步IO模型可以在数据到达后异步地通知应用程序,使应用程序可以进行其他操作而不必等待数据的拷贝和处理过程

I/O 模型

在这里插入图片描述

常用的为阻塞I/O 及 多路复用I/O

阻塞 I/O

在阻塞IO模型中,应用程序发起一个IO操作后,会一直阻塞等待,直到IO操作完成

  • 当应用程序执行IO操作时,如果数据没有准备好或无法立即写入目标,应用程序会一直等待,直到数据就绪。

阻塞IO模型是最简单的IO模型,易于理解和使用,适用于IO操作相对较少或IO时间短暂的场景

非阻塞 I/O

在非阻塞IO模型中,应用程序发起一个IO操作后,会立即返回,而不会等待IO操作的完成

  • 当应用程序执行非阻塞IO操作时,如果数据没有准备好或无法立即写入目标,应用程序会立即返回一个错误码或标识
  • 应用程序可以通过轮询或使用select、poll、epoll等函数来检查IO操作的状态,以确定数据是否已经就绪

非阻塞IO模型需要应用程序自行处理IO操作的就绪状态,适用于需要同时处理多个IO操作的场景

多路复用 I/O

多路复用IO模型通过使用select、poll、epoll等函数,将多个IO操作集中在一起进行管理。应用程序将多个IO操作注册到多路复用机制(Selector)中,并在调用多路复用函数时等待就绪的IO操作

多路复用IO模型可以同时管理多个IO操作,减少了轮询的开销,提高了效率。多路复用IO模型适用于需要同时处理大量IO操作的场景

信号驱动 I/O

信号驱动IO模型中,应用程序通过注册信号处理函数,并将文件描述符设置为非阻塞模式。

  • 当IO操作就绪时,操作系统会发送一个信号给应用程序,应用程序在信号处理函数中进行IO操作的处理

信号驱动IO模型可以在IO操作就绪时异步地通知应用程序,适用于需要异步IO操作的场景

异步 I/O

异步IO模型中,应用程序发起IO操作后,可以继续执行其他任务,而不需要等待IO操作的完成

  • 当IO操作完成后,操作系统会通知应用程序,应用程序可以异步地获取或处理已完成的IO结果

异步IO模型不需要应用程序自行管理IO操作的状态,适用于需要高性能异步IO操作的场景

I/O 模型对比

同步 I/O 与异步 I/O

  • 阻塞等待 vs 非阻塞操作:同步I/O需要应用程序阻塞等待操作完成,而异步I/O可以让应用程序在操作进行时继续执行其他任务。
  • 应用程序控制 vs 操作系统控制:同步I/O模型需要应用程序主动发起和等待I/O操作的完成,而异步I/O模型中,应用程序发起I/O操作后,操作系统负责管理和执行操作,并在完成时通知应用程序。
  • 阻塞模式 vs 异步通知:同步I/O模型中,应用程序需要等待数据就绪或写入目标后才能继续执行,而异步I/O模型中,应用程序可以在操作发起后立即返回,并在操作完成后得到通知

Java I/O 模型

BIONIOAIO
阻塞 vs 非阻塞阻塞非阻塞非阻塞
同步 vs 异步同步同步异步
线程资源消耗需要为每个连接分配独立的线程通过单线程处理多个连接利用操作系统的异步通知机制,不需要额外的线程资源
数据处理能力阻塞等待使用缓冲区(Buffer)进行数据处理,提供了更高效的数据读写能力通过异步I/O操作提供高性能的异步操作机制

BIO

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善

public class BioExample {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("Server started, waiting for connections...");

        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("Client connected: " + socket.getInetAddress());

            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            socket.close();
            System.out.println("Client disconnected.");
        }
    }
}

NIO

异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。当进行读写操作时,只须直接调用API的read或write方法

这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数

public class NioExample {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);
        System.out.println("Server started, waiting for connections...");

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                System.out.println("Client connected: " + socketChannel.getRemoteAddress());

                executorService.submit(() -> {
                    try {
                        ByteBuffer buffer = ByteBuffer.allocate(1024);

                        while (socketChannel.read(buffer) != -1) {
                            buffer.flip();
                            socketChannel.write(buffer);
                            buffer.clear();
                        }

                        socketChannel.close();
                        System.out.println("Client disconnected: " + socketChannel.getRemoteAddress());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
}

AIO

异步IO模型中,应用程序发起IO操作后,可以继续执行其他任务,而不需要等待IO操作的完成。当IO操作完成后,操作系统会通知应用程序,应用程序可以异步地获取或处理已完成的IO结果

public class AioExample {
    public static void main(String[] args) throws IOException {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        System.out.println("Server started, waiting for connections...");

        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                serverSocketChannel.accept(null, this);

                try {
                    System.out.println("Client connected: " + socketChannel.getRemoteAddress());

                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer buffer) {
                            if (result == -1) {
                                try {
                                    socketChannel.close();
                                    System.out.println("Client disconnected: " + socketChannel.getRemoteAddress());
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                return;
                            }

                            buffer.flip();
                            socketChannel.write(buffer, buffer, this);
                            buffer.clear();
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            exc.printStackTrace();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });

        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用了CompletionHandler来处理异步操作的完成和错误处理


参考资料:

  1. 《Unix网络编程第1卷》
  2. Redis之IO线程、IO多路复用,BIO、NIO和AIO区别

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Opencv实战(1)读取与图像操作

Opencv 文章目录 Opencv一、读取图片1.imshow2.namedWindow3.imshow4.效果图 二、像素操作(1).访问像素1. at()2.Mat_ (2).遍历像素1.指针遍历2.迭代器遍历 (3).threshold(4).通道分离1.split2.merge (5)Gamma矫正 三、深浅拷贝 一、读取图片 1.imshow Mat imread(const stri…

数据库所在服务器磁盘满了怎么办?

大家好&#xff0c;我是G探险者。 给大家拜个晚年哈&#xff0c;节后上班第一天&#xff0c;打开电脑&#xff0c;发现数据库服务器连不上了。 幸亏&#xff0c;节后第一天上班的人不太多&#xff0c;领导还没来&#xff0c;我一番鼓捣解决了这个问题。 所以做个总结&#xff0…

面试redis篇-02缓存穿透

原理 例&#xff1a; 一个get请求&#xff1a;api/news/getById/1 缓存穿透&#xff1a;查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库 解决方案一 缓存空数据&#xff0c;查询返回的数据为空&#xff0c;仍把…

Visual Studio Code安装Oracle SQL Developer插件

Visual Studio Code&#xff0c;简称VS Code&#xff0c;是最流行的IDE之一。SQL Developer作为面向 Oracle 数据库专业人员的查询、开发和管理工具&#xff0c;现已可作为插件&#xff08;Extension&#xff09;在VS Code中安装。无需安装 Java, .NET, 和Oracle Client 。 数…

多线程——

一、为什么要有多线程&#xff1f; 1、线程与进程 进程&#xff1a;进程是程序的基本执行实体 举例&#xff1a;在任务管理器中&#xff0c;一个软件运行之后&#xff0c;它就是一个进程 线程&#xff1a;&#xff08;简单理解&#xff0c;线程就说应用软件中互相独立&…

Positive SSL 证书介绍

Positive SSL 是一种受欢迎的 SSL 证书&#xff0c;提供了卓越的安全性、性价比和品牌信任。以下是对 Positive SSL 在这些方面的简要介绍&#xff1a; 1. 安全性&#xff1a; Positive SSL 证书采用强大的加密技术&#xff0c;确保网站和用户之间的数据传输是安全的。它使用…

PyCharm 取消所有断点

PyCharm 取消所有断点 1. Run -> View Breakpoints...2. Python Line Breakpoint3. Remove - DoneReferences 1. Run -> View Breakpoints… 2. Python Line Breakpoint ​​​ 3. Remove - Done References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

spring boot自动装配及自动装配条件判断

第一步需要在pom.xml文件指定需要导入的坐标 要是没有自动提示需要检查maven有没有 实现代码 /*springboot第三方自动配置实现方法 * 什么是自动配置 自动配置就是springboot启动自动加载的类不需要在手动的控制反转自动的加入bean中 * * *//*第一种方案包扫描 不推荐因为繁琐…

第三篇【传奇开心果系列】Python的文本和语音相互转换库技术点案例示例:pyttsx3实现语音助手经典案例

传奇开心果短博文系列 系列短博文目录Python的文本和语音相互转换库技术点案例示例系列 短博文目录一、项目背景和目标二、雏形示例代码三、扩展思路介绍四、与其他库和API集成示例代码五、自定义语音示例代码六、多语言支持示例代码七、语音控制应用程序示例代码八、文本转语音…

WouoUI-PageVersion 一个用于快速构建具有丝滑OLED_UI动画的项目

WouoUI-PageVersion 写在前面 简介&致谢 Air001的TestUI例子的b站的演示视频 Air001的LittleClock例子的b站演示视频: https://www.bilibili.com/video/BV1J6421g7H1/ Stm32的TestUI例子的b站演示视频: https://www.bilibili.com/video/BV1mS421P7CZ/ 所有演示的工程文…

黑马程序员微信小程序学习总结9.插槽(slot)、父子组件中的通信的3种方式和自定义组件behaviors

目录 自定义组件中&#xff1a;插槽&#xff08;slot&#xff09;自定义组件中&#xff1a;父子组件中的通信的3种方式属性绑定&#xff08;总结7讲过&#xff09;事件绑定&#xff08;子向父传参&#xff09;获取组件实例 自定义组件behaviors同名字段的覆盖和组合规则&#x…

Kotlin基础——泛型

泛型类型参数 编译器一般可以推导出类型实参 若创建空的list&#xff0c;则需要显示指定类型实参&#xff0c;可以用如下两种方式 val name: MutableList<String> mutableListOf()val name2 mutableListOf<String>()泛型函数 public fun <T> List<T&…

宝塔安装MySQL、设置MySQL密码、设置navicat连接

1、登录宝塔面板进行安装 2、设置MySQL连接密码 3、安装好了设置navicat连接 登录MySQL [roothecs-394544 ~]# mysql -uroot -p Enter password: 切换到MySQL数据 mysql> use mysql Database changed mysql> 查询用户信息 mysql> select host,user from user; ---…

生信学院|02月23日《ECAD数据到MCAD模型》

课程主题&#xff1a;ECAD数据到MCAD模型 课程时间&#xff1a;2024年02月23日 14:00-14:30 主讲人&#xff1a;陈冬冬 生信科技 售后服务工程师 CircuitWorks概述CircuitWorks工具栏&#xff1b;零部件库和属性信息&#xff1b;对ECAD数据的基本操作&#xff1b;将装配体输…

绝地求生:pubg全年活动整理

2023年整理&#xff0c;2024展望。2023年1月是4神兽&#xff0c;24年2月是西游&#xff0c;25年1月呢&#xff1f; 2023新3月4神兽结束后是AUG黑箱和6周年。 2024年3月也会出成长型武器黑箱和7周年。 4月&#xff1a;新通行证、战队联名、服装套装。 5月&#xff1a;是一些套装…

学习Android的第十六天

目录 Android 自定义 Adapter Adapter 接口 SpinnerAdapter ListAdapter BaseAdapter 自定义 BaseAdapter 参考文档 Android ListView 列表控件 ListView 的属性和方法 表头表尾分割线的设置 列表从底部开始显示 android:stackFromBottom 设置点击颜色 cacheColorH…

基于Java SSM框架实现精准扶贫管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现精准扶贫管理系统演示 JSP技术介绍 JSP技术本身是一种脚本语言&#xff0c;但它的功能是十分强大的&#xff0c;因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时&#xff0c;它可以使显示逻辑和内容分开&#xff0c;这就极大的方便了用户的需…

【HarmonyOS】鸿蒙开发之Slider组件——第3.5章

组件应用场景: 设备音量大小&#xff0c;调节屏幕亮度等需求 slider组件内options属性简介 value&#xff1a;滑动条当前进度值。 min&#xff1a;设置滑动条设置最小值。 max&#xff1a;设置滑动条设置最大值&#xff0c;默认为 100 。 step&#xff1a;设置滑动条滑动跳动…

遨博I20协作臂关节逆解组Matlab可视化

AUBO I20协作臂关节逆解组Matlab可视化 前言1、RTB使用注意点2、代码与效果2.1、完整代码2.2、运行效果 总结 前言 注意&#xff1a;请预先配置好Matlab和RTB机器人工具箱环境&#xff0c;本文使用matlab2022b和RTB10.04版本 工作需要&#xff0c;使用matlab实现对六轴机械臂…

初识 Rust 语言

目录 前言一、Rust 的背景二、Rust的特性三、部署开发环境&#xff0c;编写一个简单demo1、在ubuntu 20.04部署环境2、编写demo测试 四、如何看待Linux内核引入Rust 前言 自Linux 6.1起&#xff0c;初始的Rust基础设施被添加到Linux内核中。此后为了使内核驱动程序能够用Rust编…
最新文章