前言:最近在看dubbo框架底层依赖协议时注意到了java NIO,由于操作系统和I/O相关的底层原理一直没有好好研究过,感到一头雾水,于是决定写篇博客学习一下
Java IO读写原理
- read系统调用
是把数据从内核缓冲区复制到进程缓冲区
- write系统调用
把数据从进程缓冲区复制到内核缓冲区
- kernal
负责数据在内核缓冲区和磁盘之间的交换
java读写流程
客户端请求
- Linux通过网卡,读取客户断的请求数据,将数据读取到内核缓冲区
获取请求数据
- 服务器从内核缓冲区读取数据到Java进程缓冲区。
服务器端业务处理
- Java服务端在自己的用户空间中,处理客户端的请求。
服务器端返回数据
- Java服务端已构建好的响应,从用户缓冲区写入系统缓冲区。
发送给客户端
- Linux内核通过网络 I/O ,将内核缓冲区中的数据,写入网卡,网卡通过底层的通讯协议,会将数据发送给目标客户端。
同步阻塞
用户线程在系统调用的整个IO过程中都是阻塞的(1.等待数据到达内核缓冲区 2.将数据拷贝)
同步非阻塞
用户线程在系统调用的
- 等待数据到达内核缓冲区(kernal buffer) 非阻塞
- 在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息
- 用户线程需要不断的发起 I/O调用 同时可以做别的处理
- 拷贝数据到用户缓冲区(user buffer) 阻塞
多路复用模型
- 进行select/epoll系统调用,查询可以读的连接。kernel会查询所有select的可查询socket列表,当任何一个socket中的数据准备好了,select就会返回。
当用户进程调用了select,那么整个线程会被block(阻塞掉)。 - 用户线程获得了目标连接后,发起read系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),然后kernel返回结果。
- 用户线程才解除block的状态,用户线程终于真正读取到数据,继续执行。
多路复用的两种模式
优点: 用select/epoll的优势在于,它可以同时处理成千上万个连接(connection)。与一条线程维护一个连接相比,I/O多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销
异步非阻塞
AIO的基本流程是:用户线程通过系统调用,告知kernel内核启动某个IO操作,用户线程返回。kernel内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。