【IO】【Nio和IO内存交互图】

d:文件描述符的意思

 

需要了解几个概念:

 

文件描述符:也就是图上的fd,暂且可以理解为一个文件描述符对应一个socket,例如同一主机多个客户端访问同一个端口号的服务端,由于他们都来于同一个ip地址,也就是tcp的数据包,来自同一个地方,那么服务端是如何区分这些不同不通的socket呢?就是通过文件描述符来确定,这样 才可以保证数据的正确性。

在传递文件描述符的时候,会传递相应的操作类型,如读,写,接受等等,拿NIO来说就是参数SelectionKey. OP_ACCEPT等等类型。

 

文件描述符的个数:

select模型默认的fd个数的是2048个,因此对于那些只是上万连接的服务器个数就显然会很少。主要FD的个数是针对于某一个进程而言的,因此,如果想满足上万的请求,可以多启动几个进程,但是创建进程的效率损耗也是很大的。不过Linux的Poll模型就有个数的显示,他的个数是最大可以打开文件的个数限制。而NIO就是基于poll模型实现的。

 

图1:作为和IO设备数据交流的最底层,操作系统调用相应的驱动程序,来进行IO数据的读取和写入,他有两个特点:

 

第一:可以读取一个或者多个字节的数据,譬如普通IO数据读取,就是调用操作系统相应普通IO的API,然后API直接操作这块功能,bufferInputstream就是读取多个字节的情况;

第二:有数据返回数据,没有数据则阻塞IO,准确的说,操作系统调用这块功能阻塞或者返回数据,继续推导出客户端程序阻塞或者返回数据,这也是阻塞IO之所以阻塞的深层次原因;

第三:抽象理解,这里面会有各种文件描述,所有客户端的IO操作都针对于文件描述来完成。

 

图2:操作系统select功能轮询的目标内存,这块内存存储着客户端需要被监控的(从客户端传递过来)文件描述符,因此,需要监控哪些socket是由客户端传递哪些文件描述符来确定的。内核的select功能会轮询这块内存对应的IO(图1),然后可以知道哪些IO已经有数据可以读取或者写入,然后select功能服务会把这些数据复制到图3,并产生相应的事件,当客户端调用selector.select()函数时候,可以理解为轮询图2,查看是否有已经准备好的事件,如果没有任何事件,那么就会阻塞。因此,一旦select产生了事件,就会是阻塞状态的客户端进程返回,说明此时可以进行数据的读取了。

 

图3:客户端真正读取数据的目标数据块,这块内存应该在 操作系统IO内核进程的某一块内存。当客户端读取数据时候,就是直接操作这块内存,而不是像普通IO那样直接操作图1,这块数据内存的特点是,有数据了直接返回数据,没有数据直接返回空,而不是阻塞用户线程。这也是为什么NIO是非阻塞类型的原因。

 

图4:我们最熟悉的客户端,也即是java应用程序。需要注意的是任何的IO操作包括socket操作,客户端每进行一个操作,必须要传递一个文件描述符对象。然后,这个对象被操作系统获得后,进行一系列的操作(如放到图2里面进行轮询),贯穿自始到终的IO数据交互。

 

管道:channel,客户端对于一个文件描述符的操作接口,即图4到图3的某一个文件描述符的操作,就叫做一个管道,因此我们读取数据时候,常通过一个管道对象来进行数据读取,如socketChnanel.read()

 

 

 

select/poll模型和epoll模型的区别以及epoll模型的引入

 

          NIO是基于OS内核的select/pool模型来进行实现的,如图2所描述,OS会轮询fd的集合,来确定那些fd已经处于就绪状态,问题来了,如果这个fd的集合特别大,任意时间只有部分的socket是活跃的,但是select/poll模型都会线性扫描全部的集合,导致效率直线下降。我们有没有其他的机制让效率更高效呢?可以这样思考,对于select模型,当某个文件描述符就绪时候,他是主动去线性的轮询并检测文件描述符,试想,如果文件描述符在自己准备就绪的情况下要是能主动的触发一些操作,不就可以避免select全面扫描select集合的弊端了吗?所以伟大的科学家门就发明了这样一种IO模型,即epoll模型。

     epoll模型时基于callback函数实现的。他只对活跃的socket进行操作,换句话说就是那些socket对应的fd已经处于就绪状态,由于epoll模型的每个fd都有一个回调函数,这样fd一旦就绪(本质上是TCP缓冲区有数据),就会调用这个回调函数,进而通知客户端,这样就可以避免对不活跃的socket进行轮询检测的弊端了。这些功能都是内核帮助实现的,所以也可以成为一个“伪”AIO。需要注意的是,由于有回调函数的异步操作,所以效率比select还是稍微慢一点,因此,如果所有的socket都是活跃的,如一个高速的lan环境,此时epoll并不比selec效率高很多,但是一旦有空闲的连接如WAN环境,epoll的效率就会很高。

 

 

NIO和IO区别分析(http://ifeve.com/java-nio-vs-io/)

 

面向流和面向缓冲

 

IO面向流是因为他直接操作图1,调用相应的IO接口从底层读取数据,

Nio面向缓冲,是因为NIO直接操作的是图3

 

阻塞和非阻塞

 

IO阻塞 是因为直接操作图1,有数据返回,无数据阻塞

NIO非阻塞  是因为直接 操作图3,有数据返回,无数据返回空

 

有无选择器

 

Nio之所以拥有以上两个特区别,正是因为他拥有了选择器,其实准确的说,应该是调用了操作系统底层的select函数模型,从而利用操作系统的select功能来实现面向缓冲区的无阻塞IO操作。

 

影响应用程序设计

 

 

服务端都在单线程的环境下比较:

由于阻塞IO当某个请求没数据时候,服务端这个线程将会阻塞,不能处理其他请求,而NIO如果客户端没有数据,让就可以立马返回,进而处理其他的连接。当然,阻塞IO常用的做法是单个线程接收请求,然后每一个请求开辟一个线程来处理,但是如果请求特别多,那么,服务端开辟过多的线程反而会出现性能的下降。因此,NIO的优势在于服务端可以用很少的线程来处理大量的并发连接。

对于阻塞IO,如果客户端的连接数不是太多,且是长连接,一次发送大量的数据,反而阻塞IO会获得更好的性能优势,因为一个线程对应一个SOCKET请求,那么他就会专门用于处理这个请求。

 

 

 

总结:

阻塞IO如果某一个客户端没有数据可以读写,那么服务端的线程就会阻塞,可以试想,有没有什么方法可以实现当有数据的时候我应用程序的服务端来读取数据,没有数据的时候我服务端线程可以做其他的事情,而不是处于阻塞状态,这样可以大大提高服务端的使用率。在这样一种背景下,操作系统提出了select功能,这个功能来完成所有客户端需要监控的socket(文件描述符),然后专门构造一块数据内存,来存放IO接口过来的数据, 这样让客户端操作这块内存,就可以实现有数据读取,无数据返回的效果。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值