java nio原理 epoll_多路复用 Select Poll Epoll 的实现原理(BIO与NIO)
BIO
blocking阻塞的意思,当我们在后端开发使用的时候,accetp 事件会阻塞主线程。
当accept事件执行的时候,客户的会和服务建立一个socket 连接。一般后端就会开启一个线程执行后续的读写操作。这里其实就一个多线程进行的CS模型。
这种模型很难做到C10K,比如说1w个客户就需要1w个线程去支持,且不说线程能否开启这么多,就cpu 估计就爆了。线程上下文切换也会把整个机器性能拉爆了。
常见的基本写法:public class TestBIOServer {
private static final ExecutorService executorPool = Executors.newFixedThreadPool(5);
private static class Handler implements Runnable{
private Socket clientSocket;
public Handler(Socket clientSocket){
this.clientSocket = clientSocket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(
clientSocket.getOutputStream(), true);
char chars[] = new char[64];
int len = reader.read(chars);
StringBuffer sb = new StringBuffer();
sb.append(new String(chars, 0, len));
System.out.println('From client: ' + sb);
writer.write(sb.toString());
writer.flush();
} catch (IOException e) {
e.printStackTrace();
try {
clientSocket.close();
} catch (IOException ex) {
// ignore on close
}
}
}
}
public void serve(int port) throws IOException {
final ServerSocket socket = new ServerSocket(port);
try {
while (true) {
long beforeTime = System.nanoTime();
final Socket clientSocket = socket.accept();
System.out.println('Establish connection time: '+ (System.nanoTime()-beforeTime)+' ns');
executorPool.execute(new Handler(clientSocket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException{
TestBIOServer server = new TestBIOServer();
server.serve(8086);
}
}
NIO 如何处理的呢 ?
从java层面来看,NIO 提供一套非阻塞的接口,编程方式。
其实就是不需要每一个客户端请求生成一个线程去处理。
NIO 的非阻塞特性就可以用一个线程检查N 个 socket状态。java在NIO包为我们提供一个selector ,我们只需要把我们想监听的socket注册到这个selector 中,
主线程只阻塞在这个selector 的select ,当某个socket 就绪好了,就可以换线主线程,通过这个selector 获取想对应就绪状态的socket,就可以对当前socket操作了。
常见的写法:public class TestNIOServer {
private static final int port = 12345;
private static final int buffer_size = 1024;
private static final String charsetName = 'UTF-8';
private static final String hello_message = 'hello client !';
public static void main(String[] args) throws Exception {
//获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(port));
//获取通道管理器
Selector selector = Selector.open();
//将通道注册通道管理器的OP_ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
//当有注册事件到达时,方法返回,否则阻塞
selector.select();
Iterator iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if(!selectionKey.isValid()) {
continue;
}
if(selectionKey.isAcceptable()) {
System.out.println('accept a connection...');
ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}else if(selectionKey.isReadable()) {
System.out.println('accept a message...');
SocketChannel sc = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(buffer_size);
int length = sc.read(byteBuffer);
if(length > 0) {
String receive_message = new String(byteBuffer.array(), charsetName);
System.out.println('server accept : '+receive_message);
sc.write(ByteBuffer.wrap(hello_message.getBytes(charsetName)));
}
}
iterator.remove();
}
}
}
}
从内核层面的IO 在聊聊这个BIO NIO
底层其实还是native函数的调用,JVM的调用系统函数实现的。
底层的多路复用 select,poll epoll 这几个版本的 的实现原理和比较
kernel 提供的select 函数的原理:
1. 系统每次调用select 函数 都会涉及到一个 内核态到用户太的切换
2.每次都需要检查socket的状态集合,其实就是检查fd状态,内核中文件句柄。这里复杂度是O(N),
如果有就绪状态的socket 就直接返回 不会阻塞当前线程,(并且在就是的文件句柄设置一个标记mask),返回的是一个int,表示有几个socket就绪了,但是具体的那些socket 就绪了 java 层面并不知道。后续java 想知道具体的就绪的socket 还得一个O(N)复杂度调用。
如果没有就说明当前没有就绪好的socket. 这个时候就会阻塞当前线程 直到有就绪的socket
3.select函数监听socket,对监听socket总数最多1024个,这里fd_set 是select的函数参数之一,
他本质就是一个bitmap位图结构,这个默认长度就是1024。默认值给一个1024也是出于性能的考虑,每次调用的都是一个ON复杂度,加上内核态用户太切换,两次O(N)复杂度调用。系统调动涉及到参数拷贝,如果太大也不利于系统调用参数拷贝。
4.select 函数如何通知java 线程 有就绪的socket 的呢 ?(这个问题其实设计到很多计算机原理知识,这里铺垫先说两个知识点)
操作系统调度 :CPU 同一时刻只能 运行一个进程。操作系统核心能力就是调度,N个进程,让他们在操作系统上切换执行。没有挂起的进程,都在工作队列中,他们都有机会获得CPU的执行机会。挂起的进程其实就是移出工作队列,在java 层面就是阻塞了。linux系统线程其实就是轻量级的进程。
操作系统中断 :比如我们在用键盘打字,如果CPU 一直被其他进程占用,那我们就没法打字了。但是实际情况不是这样的。这个就是因为有系统中断的曾在,当你按下按键之后 就会触发一个电流信号 这个信号给了主板 主板就会触发一个中断操作,中断的本质就是让当前CPU 保存执行上下文,并让出CPU给当前中断程序,比如键盘的中断程序 就执行键盘的输出逻辑。
总结一下 select 过程
第一阶段:
select 函数在一轮 轮询中没有发现就绪的socket ,就会把当前的进程 保留到需要检查socket的等待队列中。
socket 本身结构有三块核心区域 :读缓存,写缓存,等待队列
select 函数把当前进程保留到需要检查 socket 等待队列之后,就把当前进程从工作队列中移出。移除之后其实就是挂起当前select 进程不在执行。
第二阶段
假设连接的socket 的客户端像服务器发送一个数据,这个数据通过网络,到当前计算的网卡上,网卡到DMA 硬件这种方式直接将数据写入内存里头,整个过程CPU 不参与,当数据完成传输以后,他就是会 触发网络数据传输完成的中断程序。这个中断程序 就会根据内存中他有的数据包 分析出数据包是那个socket,根据TCP/IP协议,他又保证传输的时候有端口号,然后根据端口号,找到对应的socket 实例,找到实例之后就把数据导入到 socket 的缓冲区里头,导入完成以后,检查socket的等待队列,是不是有等待,如果有等待socket,就把等待的移动到 工作队列中。中断程序就执行完了。
我们进程进入到工作队列了 就有机会获取CPU时间片了。
POLL原理
其实和select 原理差不多,最大的区别就是传入参数不一样,select 使用的事 bitmap 表示需要检查的socket,poll 传入的是数组结构,
主要是为了解决 select 的bitmap 长度是1024问题。数组就没有限制了 可以监控超过1024个socket。
Epoll 原理:
技术产生背景:主要是解决select 和poll 函数的缺陷,
第一个缺陷:每次调用 都需要提供所有的监听的 socket 文件描述符的集合。而且我们程序是死循环调用 select poll 函数的。用户态 和内核态 的数据拷贝,这个比较消耗性能。其实实际情况可能是监听几百个socket ,每次只有1-2个fd 状态改变了,但是依然需要轮询全部的socket。因为select 和poll 只是一个很单纯的函数。他在kernel 层面不会保留任何缓存数据,所以每次调用只能数据拷贝了。
第二个缺陷:select 和poll 函数他们的返回值是int,只能代表有几个socket 就绪了,或者有错误了。没法知道是具体的那个socket就绪了。
导致我们程序被唤醒之后,还需要新一轮的调用检查socket,这里右需要用户态内核态调用和数据拷贝。
epoll 产生 主要是解决上面两个问题
解决上面两个问题 就需要再内核空间,他有一个对应的数据结构去存这些数据,这样就不需要每次拷贝数据,每次再去轮询就绪socket。新的数据结构 :eventpoll 对象。epoll_create() 创建这个对象,返回一个id 其实就是 epfd 的文件号。在内核中开辟了一小块空间,并且我们知道这个空间的地址。
eventpoll : 主要有两个重要区域, 一块是 socket_fd 监听列表, 一块是 socket_fd 就绪列表
提供两个函数 : epoll_ctl 增删改成 fd 列表
epoll_wait 阻塞调用线程,返回就绪的fd
java nio原理 epoll_多路复用 Select Poll Epoll 的实现原理(BIO与NIO)相关推荐
- python3 异步 非阻塞 IO多路复用 select poll epoll 使用
有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的. 下面记录下分别基于Select/Poll/Epoll的echo ser ...
- Python异步非阻塞IO多路复用Select/Poll/Epoll使用
来源:http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理 ...
- IO多路复用select/poll/epoll详解以及在Python中的应用
IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...
- IO多路复用select,poll epoll以及区别
看这个一次读懂 Select.Poll.Epoll IO复用技术 文章来简单理解下,如果不是很明白的话,可以参考下面转的知乎上面白话文列子 作者:Leslie 链接:https://www.zhihu ...
- python poll_python IO 多路复用 select poll epoll
select select 原理 select 是通过系统调用来监视着一个由多个文件描述符(file descriptor)组成的数组,当select()返回后,数组中就绪的文件描述符会被内核修改标记 ...
- socket 编程篇六之IPO多路复用-select poll epoll
http://blog.csdn.net/woxiaohahaa/article/details/51498951 文章参考自:http://blog.csdn.net/tennysonsky/art ...
- IO模型(select, poll, epoll的区别和原理)
参考<unix网络编程> 参考http://blog.csdn.net/blueboy2000/article/details/4485874 参考http://blog.csdn.net ...
- select poll epoll IO操作多路复用及猴子补丁
一:select(能监控数量有限,不能告诉用户程序具体那个连接有数据) select目前几乎所有的平台都支持,其良好的跨平台支持也是一个优点 select的缺点在于单个进程能够监控的文件描述的数量存在 ...
- 【C/C++服务器开发】文件,文件描述符,I/O多路复用,select / poll / epoll 详解
文章目录 一.前言 1.文件的概念 2.文件描述符和文件指针 文件描述符 文件描述符和文件指针的区别 文件描述符太多了怎么办 二.I/O多路复用 1.I/O多路复用的由来 不要打电话给我,有需要我会打 ...
最新文章
- 开箱即用的VScode C++环境
- DL之perceptron:利用perceptron感知机对股票实现预测
- Android属性动画 TypeEvaluator
- Vitamio中文API文档(1)—— MediaStore
- hdu 1251 统计难题 (Trie树)
- 有多少小微餐饮创业者陷入了“就业型创业”的死亡漩涡而不自知?
- 中国量子技术产出居世界第二 英国量子技术未来5个发展方向
- python切割音频文件_python3使用pydub切分音频文件
- cmake 安装不同版本
- janusgraph源码分析1-下载编译启动
- opendss视频教程
- 特征选择relief算法介绍
- h5调用手机相册摄像头以及文件夹
- flashpaper java_基于FlashPaper实现JSP在线阅读代码示例
- 从URDF到KDL(C++Python)
- STM32单片机初学2-从Keil工程创建开始
- Java中有指针么?
- 怎么判断数字n是否为2的x次方,即2的幂次呢,比如2,4,8,16,32
- 国美被曝停发员工工资;支付宝正式接入鸿蒙生态;推特遭到集体诉讼;小马智行业务调整,多位高管离职 | 每日大事件...
- 设备接入交换机后无法ping通问题处理
热门文章
- 【TensorFlow2.0】(7) 张量排序、填充、复制、限幅、坐标选择
- windows下opencv安装及配置(vs2010环境)
- 微信支付android不弹出支付密码窗口,微信支付没弹出支付窗口
- 处有未经处理的异常:0xC0000005 : 读取位置 0x00000000 时发生访问冲突。
- js去el的map_转:el表达式获取map对象的内容 js中使用el表达式 js 中使用jstl 实现 session.removeattribute...
- python映射类型有哪些_什么是python中唯一的映射类型
- UE5虚幻引擎5中的实时特效学习 Introduction to real time FX in Unreal Engine 5
- Rocksdb 的优秀代码(一) -- 工业级分桶算法实现分位数p50,p99,p9999
- vim 成“神“之路 (一)
- 在做项目中遇到的JS问题