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)相关推荐

  1. python3 异步 非阻塞 IO多路复用 select poll epoll 使用

    有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的. 下面记录下分别基于Select/Poll/Epoll的echo ser ...

  2. Python异步非阻塞IO多路复用Select/Poll/Epoll使用

    来源:http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理 ...

  3. IO多路复用select/poll/epoll详解以及在Python中的应用

    IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...

  4. IO多路复用select,poll epoll以及区别

    看这个一次读懂 Select.Poll.Epoll IO复用技术 文章来简单理解下,如果不是很明白的话,可以参考下面转的知乎上面白话文列子 作者:Leslie 链接:https://www.zhihu ...

  5. python poll_python IO 多路复用 select poll epoll

    select select 原理 select 是通过系统调用来监视着一个由多个文件描述符(file descriptor)组成的数组,当select()返回后,数组中就绪的文件描述符会被内核修改标记 ...

  6. socket 编程篇六之IPO多路复用-select poll epoll

    http://blog.csdn.net/woxiaohahaa/article/details/51498951 文章参考自:http://blog.csdn.net/tennysonsky/art ...

  7. IO模型(select, poll, epoll的区别和原理)

    参考<unix网络编程> 参考http://blog.csdn.net/blueboy2000/article/details/4485874 参考http://blog.csdn.net ...

  8. select poll epoll IO操作多路复用及猴子补丁

    一:select(能监控数量有限,不能告诉用户程序具体那个连接有数据) select目前几乎所有的平台都支持,其良好的跨平台支持也是一个优点 select的缺点在于单个进程能够监控的文件描述的数量存在 ...

  9. 【C/C++服务器开发】文件,文件描述符,I/O多路复用,select / poll / epoll 详解

    文章目录 一.前言 1.文件的概念 2.文件描述符和文件指针 文件描述符 文件描述符和文件指针的区别 文件描述符太多了怎么办 二.I/O多路复用 1.I/O多路复用的由来 不要打电话给我,有需要我会打 ...

最新文章

  1. 开箱即用的VScode C++环境
  2. DL之perceptron:利用perceptron感知机对股票实现预测
  3. Android属性动画 TypeEvaluator
  4. Vitamio中文API文档(1)—— MediaStore
  5. hdu 1251 统计难题 (Trie树)
  6. 有多少小微餐饮创业者陷入了“就业型创业”的死亡漩涡而不自知?
  7. 中国量子技术产出居世界第二 英国量子技术未来5个发展方向
  8. python切割音频文件_python3使用pydub切分音频文件
  9. cmake 安装不同版本
  10. janusgraph源码分析1-下载编译启动
  11. opendss视频教程
  12. 特征选择relief算法介绍
  13. h5调用手机相册摄像头以及文件夹
  14. flashpaper java_基于FlashPaper实现JSP在线阅读代码示例
  15. 从URDF到KDL(C++Python)
  16. STM32单片机初学2-从Keil工程创建开始
  17. Java中有指针么?
  18. 怎么判断数字n是否为2的x次方,即2的幂次呢,比如2,4,8,16,32
  19. 国美被曝停发员工工资;支付宝正式接入鸿蒙生态;推特遭到集体诉讼;小马智行业务调整,多位高管离职 | 每日大事件...
  20. 设备接入交换机后无法ping通问题处理

热门文章

  1. 【TensorFlow2.0】(7) 张量排序、填充、复制、限幅、坐标选择
  2. windows下opencv安装及配置(vs2010环境)
  3. 微信支付android不弹出支付密码窗口,微信支付没弹出支付窗口
  4. 处有未经处理的异常:0xC0000005 : 读取位置 0x00000000 时发生访问冲突。
  5. js去el的map_转:el表达式获取map对象的内容 js中使用el表达式 js 中使用jstl 实现 session.removeattribute...
  6. python映射类型有哪些_什么是python中唯一的映射类型
  7. UE5虚幻引擎5中的实时特效学习 Introduction to real time FX in Unreal Engine 5
  8. Rocksdb 的优秀代码(一) -- 工业级分桶算法实现分位数p50,p99,p9999
  9. vim 成“神“之路 (一)
  10. 在做项目中遇到的JS问题