计算机IO系列(二)BIO/NIO/多路复用实现

一、什么是IO?

我们都知道Liux世界里、一切皆文件、而文件是什么呢?文件就是一串二进制流而已、不管socket、还是FIFO、管道、终端、对我们来说、一切都是文件、一切都是流、在信息交换的过程中、我们都是对这些流进行数据的收发操作、简称为I/O操作(input and output)、往流中读出数据、系统调用read、写入数据、系统调用write、不过话说回来了、计算机里有这么多的流、我怎么知道要操作哪个流呢?做到这个的就是文件描述符、即通常所说的fd、一个fd就是一个整数、所以对这个整数的操作、就是对这个文件(流)的操作、我们创建一个socket、通过系统调用会返回一个文件描述符、那么剩下对socket的操作就会转化为对这个描述符的操作、不能不说这又是一种分层和抽象的思想。

二、IO交互

通常用户进程中的一个完整IO分为两个阶段

用户空间<------------->内核空间、

 内核空间<------------->设备空间、

 内核空间中存放的是内核代码和数据、而进程的用户空间中存放的是用户程序的代码和数据、不管是内核空间还是用户空间、它们都处于虚拟空间中、Linux使用两级保护机制:0级供内核使用、3级供用户程序使用、操作系统和驱动程序运行在内核空间、应用程序运行在用户空间、两者不能简单地使用指针传递数据、因为Linux使用的虚拟内存机制、其必须通过系统调用请求kernel来协助完成IO动作、内核会为每个IO设备维护一个缓冲区、用户空间的数据可能被换出、当内核空间使用用户空间指针时、对应的数据可能不在内存中。

对于一个输入操作来说、进程IO系统调用后、内核会先看缓冲区中有没有相应的缓存数据、没有的话再到设备中读取、因为设备IO一般速度较慢、需要等待、内核缓冲区有数据则直接复制到进程空间、

所以、对于一个网络输入操作通常包括两个不同阶段:

(1)等待网络数据到达网卡 –&gt; 读取到内核缓冲区

(2)从内核缓冲区复制数据 –&gt; 用户空间

IO有内存IO、网络IO和磁盘IO三种、通常我们说的IO指的是后两者。

阻塞IO(blocking I/O)

我们运行一段服务端socket监听程序(典型的阻塞IO场景):

我们知道server.accept是阻塞的,如果没有连接连上来就会一直等待不会往下执行。

同时我们是道reader.readLIne也是阻塞的,不写入东西也不会往下执行。所以我们new了个线程,可以达到同时监听多个连接的目的。

其实网络通信过程中的系统调用:前面两个函数的阻塞的根因是因为内核的accept和recv的系统调用是阻塞调用,所以会有BIO。

这段程序中涉及到的系统调用如下:

java的bio对应的包是:java.io.*

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

2.非阻塞IO(noblocking I/O)

java的nio对应的:java.nio.*(jdk1.4之后才有)

import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

下面是一段典型的java nio服务端的代码:其中

ss.configureBlocking(false)//很重要,表示设置为非阻塞io

ss.accept在非阻塞模式下不会阻塞,

  • 非阻塞模式:在调用accept方法后,如果无连接建立,则返回null(实际上系统调用的返回时-1,java返回时null);如果有连接,则返回SocketChannel。

我们就达到一个线程监听多个请求的作用。前面的BIO需要多个线程才能同时监听到多个请求。

SocketChannel简述:ServerSocketChannel简述_weixin_33951761的博客-CSDN博客

     注意for循环需要遍历所有连接,向内核发送recv系统调用,系统调用会产生软中断造成用户态内核态上下文切换,有很多无效系统调用。那怎么很容易想到减少系统调用的次数。

所以我们有了多路复用器,进程发生系统调用前,先去查下有多少个可以读:

IO多路复用

     目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现。关于io多路复用可参考此文:网络通信 --> IO多路复用之select、poll、epoll详解 - 蚂蚁吃大象、 - 博客园

对于 select 这种⽅式,需要进⾏ 2 次「遍历」⽂件描述符集合,⼀次是在内核态⾥,⼀个次是在⽤户态⾥ ,⽽且还会发⽣ 2 次「拷⻉」⽂件描述符集合,先从⽤户空间传⼊内核空间,由内核修改后,再传出到⽤户空间中。

select 使⽤固定⻓度的 BitsMap ,表示⽂件描述符集合,⽽且所⽀持的⽂件描述符的个数是有限制的,在Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最⼤值为 1024 ,只能监听 0~1023 的⽂件描述符。poll 不再⽤ BitsMap 来存储所关注的⽂件描述符,取⽽代之⽤动态数组,以链表形式来组织,突破了
select 的⽂件描述符个数限制,当然还会受到系统⽂件描述符限制。
但是 poll select 并没有太⼤的本质区别, 都是使⽤「线性结构」存储进程关注的 Socket 集合,因此都需要遍历⽂件描述符集合来找到可读或可写的 Socket ,时间复杂度为 O(n) ,⽽且也需要在⽤户态与内核态之间拷⻉⽂件描述符集合 ,这种⽅式随着并发数上来,性能的损耗会呈指数级增⻓

高版本的jdk主要是用的是epoll系统调用:epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次

基本原理:epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。Epoll对于多核来说很友好,相对于前面两个系统调用,多了create和ctl,意味这不用每次都传很多的文件描述符,但是在内核里面增加了两块空间。空间换时间的做法。

java实际上用的是操作系统的系统调用来实现的自己的多路复用,也就是操作系统的多路复用是java的多路复用的基础。

Reactor模型

刚刚的select和epoll都是关注某个io连接产生的事件。但是实际上,我们在处理io的时候往往最关注的有相关业务的处理,并且我们关注的也不是网络io的处理,而是之关心某个事件触发然后执行相应的业务逻辑。所以我们需要封装一层,将事件和IO多路复用抽象出来,我们自己可以选择自己的实现,来方便我们进行IO编程。使得的IO编程更加灵活。

组成:阻塞IO+IO多路复用

特征:以事件循环、事件驱动、事件回调来实现业务逻辑处理

Reactor模式的抽象:

a, Handle表示句柄,文件描述符、socket等; 实际上就是对IO事件的抽象。实际上就是对fd进行了包装。

b, EventDemultiplexer表示多路分发机制,调用系统提供的多IO路复用,比如select,epoll。 程序先将关注的句柄注册到EventDemultiplexer,当有相关事件到来触发EventDemultiplexer通知程序。

c, EventHandler定义事件处理方法,

d, Reactor是事件管理的接口,注册和销毁事件,并运行事件循环,当EventDemultiplexer返回Handle有事件"就绪",将其分发给EventHandler上对应的方法。

e, ConcreteEventhandler实现每个事件的处理逻辑。

Netty是典型的Reactor模型结构,关于Reactor的详尽阐释,本文站在巨人的肩膀上,借助 Doug Lea(就是那位让人无限景仰的大爷)的“Scalable IO in Java”中讲述的Reactor模式。

“Scalable IO in Java”的地址是:http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

 

​​​​​​​ 

此文来自于网课记录一下

历程:

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值