ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java IO模型

2021-09-11 13:01:19  阅读:100  来源: 互联网

标签:异步 Java socket 模型 阻塞 内核 IO 缓冲区


Java IO读写原理

内存是存储和CPU打交道的数据,在Linux上内存主要分为两部分(内核部分和用户部分)。
read系统调用,并不是把数据直接从物理设备,读数据到内存。write系统调用,也不是直接把数据,写入到物理设备。
read系统调用,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操作系统kernel内核完成的。

内核缓冲区和进程缓冲区

缓冲区的目的,是为了减少频繁的系统IO调用。
有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到用户缓冲区,write把数据从用户缓冲区复制到内核缓冲区中。等待缓冲区达到一定数量的时候,再进行IO的调用,提升性能。

至于什么时候读取和存储则由内核来决定,用户程序不需要关心。在linux系统中,系统内核也有个缓冲区叫做内核缓冲区。每个进程有自己独立的缓冲区,叫做进程缓冲区。所以,用户程序的IO读写程序,大多数情况下,并没有进行实际的IO操作,而是在读写自己的进程缓冲区。

Java 读写IO的底层流程

用户程序进行IO的读写,基本上会用到系统调用
read&write,read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区。

在这里插入图片描述
典型的Java服务器处理网络请求的过程:
客户端请求:通过网卡,读取客户端的请求的数据,将数据读取到内核缓冲区。
服务端用户程序请求数据:在内核缓冲区的数据在读取到用户缓冲区。

同步/异步、阻塞/非阻塞介绍

介绍IO模型前需要明白同步/异步、阻塞/非阻塞
同步(Synchronization) / 异步(Asynchronization)
同步和异步都是基于应用程序与操作系统处理 IO 事件所采用的方式,比如:
同步:是应用程序要直接参与 IO 读写的操作。
异步:所有的 IO 读写交给操作系统去处理,应用程序只需要等待通知。
同步方式在处理 IO 事件的时候,必须阻塞在某个方法上面等待我们的 IO 事件完成(阻塞 IO 事件或者通过轮询 IO 事件的方式)。
对于异步来说,所有的 IO 读写都交给了操作系统。这个时候,我们可以去做其他的事情,并不需要去完成真正的 IO 操作,当操作完成 IO 后会给我们的应用程序一个通知。
阻塞(Block) / 非租塞(NonBlock)
阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪的一种处理方式,比如当数据没有准备就绪的时候。
阻塞:往往需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里。
非阻塞:当我们的进程访问我们的数据缓冲区的时候,如果数据没有准备好则直接返回,不会等待。如果数据已经准备好,也直接返回。
能够组成的模型分为同步阻塞、同步非阻塞、异步阻塞、异步非阻塞,不存在同步异步、阻塞非阻塞

常见IO模型

对于一次 IO 访问,它会经历两个阶段:等待数据准备就绪 (Waiting for the data to be ready);将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。
举例来说:
读函数:分为等待系统可读和真正的读。
写函数:分为等待网卡可以写和真正的写。
说明:等待就绪的阻塞是不使用 CPU 的,是在“空等”。而真正的读写操作的阻塞是使用 CPU 的,真正在“干活”,而且这个过程非常快,属于 memory copy,宽带通常在 1GB/s 级别以上,可以理解为基本不耗时。
在这里插入图片描述
-阻塞I/O
-非阻塞I/O
-I/O复用
-信号驱动I/O
-异步I/O

阻塞I/O模型

在这里插入图片描述

非阻塞I/O模型

非阻塞IO通过进程反复调用IO函数
采用轮询,占用CPU
在这里插入图片描述### I/O复用
主要是select和epoll,对一个IO端口,两次调用,两次返回,能实现对多个IO端口进行监听。
多个连接共用一个等待机制。在这里插入图片描述

信号驱动IO模型

首先开启套接口信号驱动IO功能,通过系统调用sigation执行一个信号处理函数(此信号调用直接返回,进程继续工作)。
当数据准备就绪时,生成一个signal信号,通知应用程序来取数据。
在这里插入图片描述

异步IO

告知内核启动某个操作,并让内核在整个操作完成的的那个之后(将数据从内核复制到用户自己的缓冲区),进行通知。
和信号驱动模型的主要区别:信号驱动IO由内核通知我们合适可以开始一个IO操作
在client进行IO操作的时候需要等待,所以是同步的。异步IO模型由内核通知我们IO何时已经完成,client不需要进行IO的处理了,所以是异步的。
在这里插入图片描述
一个形象的示例说明:
活动:演唱会
角色1:举办商 售票业务员
角色2:黄牛
角色3:小明
角色4:送票快递员
同步阻塞 :

小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来。三天以后才出来,
小明直接打了个地铺睡在举办商售票大厅,一直等票出来,然后买票。
同步非阻塞 : socket可以设置non-blocking
小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来,然后小明走了,办理其他事情去了,
然后过了2个小时,又去举办商售票大厅买票来了,如果票还没有出来,小明又先去办其他事情了,
重复上面的操作,直到有票可以买。
i/o复用: java selector() => linux epoll_wait() 同步非阻塞I/O
小明想买票看演唱会,都直接给黄牛(selector/epoll)打电话了,说帮我留意买个票,
票买了通知我,我自己去取(当我接到黄牛的电话时,我需要花费整个路成的时间去读这个数据,买拿这个票),
那么票没出来之前,小明完全可以做自己的事情。
信号i/o:
小明想买票看演唱会,给举办商售票业务员说,给给你留个电话,有票了请你给我打个电话通知一下 (是看人家操作系统提不提供这种功能,Linux提供,windows没有这种机制),我自己再来买票
(小明完全可以做自己的事情,但是票还是需要小明自己去拿的)
异步非阻塞i/o: Linux aio_read aio_write
小明想买票看演唱会,给举办商售票业务员说(异步非阻塞i/o)打电话了,给你留个地址,有票了请通知快递员,
把这张票送到这个地址来,当小明听到敲门声,看见快递员,就知道票好了,而且指导票好了的时候,
票已经到他手上了,票不用小明自己去取(应用不用自己再去read数据了)

Socket编程

在这里插入图片描述
网络编程:
服务端需要等待客户端连接,服务需要先于客户端进行启动网络编程通信必须需要IP和端口。
服务端流程:
1、服务端需要先启动ServerSocket实例
2、绑定端口
3、等待客户端的连接
4、进行读写操作
5、关闭资源

客户端的流程:
1、创建Socket实例
2、连接服务端Connect(服务端的IP+端口 “localhost/127.0.0.1/192.168.43.11”)
3、进行读写操作
4、关闭资源

/**
 * desc:服务端
 * @user:西财彭于晏
 */

public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            //创建ServerSocket实例
            serverSocket = new ServerSocket();

            //绑定端口
            serverSocket.bind(new InetSocketAddress(6666));
            System.out.println("服务端启动成功啦");

            //等待客户端的连接:accept(),获取用户的连接是socket,进行读写操作的是socket,不是ServerSocket
            Socket socket = serverSocket.accept();
            System.out.println("有客户端连接啦");

            //读写操作
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[100];
            int len = 0;
            while ((len = inputStream.read(bytes)) != -1) {
                System.out.println("客户端发送消息:"+new String(bytes,0,len));
            }


            System.out.println("服务端关闭啦");
            //关闭资源
            socket.close();
            inputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
/**
 * desc: 客户端
 * @user:西财彭于晏
 */

public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            //创建socket实例
            socket = new Socket();


            //连接服务端 :connect
            socket.connect(new InetSocketAddress("127.0.0.1", 6666));
            System.out.println("连接服务端成功");

            //读写操作
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("杨柯真帅!".getBytes());
            System.out.println("客户端已发送数据");


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if (socket != null) {
                try {
                    System.out.println("关闭资源");
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果
客户端:
在这里插入图片描述
服务端:
在这里插入图片描述

标签:异步,Java,socket,模型,阻塞,内核,IO,缓冲区
来源: https://blog.csdn.net/m0_46853673/article/details/120235988

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有